diff options
| author | Zephyrrus <[email protected]> | 2021-01-05 00:27:36 +0200 |
|---|---|---|
| committer | Zephyrrus <[email protected]> | 2021-01-07 10:47:16 +0200 |
| commit | 9370c3218281796c7c0864ab6b39e847572cf35c (patch) | |
| tree | 567f1ba7c22d2f31167e7c1d4779f7a3b3c8a503 /src/api/routes/admin | |
| parent | feat: check for real mimetype using file-type (diff) | |
| download | host.fuwn.me-9370c3218281796c7c0864ab6b39e847572cf35c.tar.xz host.fuwn.me-9370c3218281796c7c0864ab6b39e847572cf35c.zip | |
feat: Add experimental stats endpoint (no cache system yet)
Diffstat (limited to 'src/api/routes/admin')
| -rw-r--r-- | src/api/routes/admin/statsGET.js | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/src/api/routes/admin/statsGET.js b/src/api/routes/admin/statsGET.js new file mode 100644 index 0000000..a505f2d --- /dev/null +++ b/src/api/routes/admin/statsGET.js @@ -0,0 +1,208 @@ +const Route = require('../../structures/Route'); +const Util = require('../../utils/Util'); +const si = require('systeminformation'); + +// TODO: Implement a cache system that can be reset by other endpoints +const statsCache = { + system: null, + fileSystems: null, + uploads: null, + users: null, + albums: null +}; + +// Thank you Bobby for the stats code https://github.com/BobbyWibowo/lolisafe/blob/safe.fiery.me/controllers/utilsController.js +class filesGET extends Route { + constructor() { + super('/admin/stats', 'get', { adminOnly: true }); + } + + async getSystemInfo() { + const os = await si.osInfo(); + + const currentLoad = await si.currentLoad(); + const mem = await si.mem(); + const time = si.time(); + const nodeUptime = process.uptime(); + + return { + 'Platform': `${os.platform} ${os.arch}`, + 'Distro': `${os.distro} ${os.release}`, + 'Kernel': os.kernel, + 'CPU Load': `${currentLoad.currentload.toFixed(1)}%`, + 'CPUs Load': currentLoad.cpus.map(cpu => `${cpu.load.toFixed(1)}%`).join(', '), + 'System Memory': { + value: { + used: mem.active, + total: mem.total + }, + type: 'byteUsage' + }, + 'Memory Usage': { + value: process.memoryUsage().rss, + type: 'byte' + }, + 'System Uptime': { + value: time.uptime, + type: 'time' + }, + 'Node.js': `${process.versions.node}`, + 'Service Uptime': { + value: Math.floor(nodeUptime), + type: 'time' + } + }; + } + + async getFileSystemsInfo() { + const stats = {}; + + const fsSize = await si.fsSize(); + for (const fs of fsSize) { + stats[`${fs.fs} (${fs.type}) on ${fs.mount}`] = { + value: { + total: fs.size, + used: fs.used + }, + type: 'byteUsage' + }; + } + + return stats; + } + + async getUploadsInfo(db) { + const stats = { + 'Total': 0, + 'Images': 0, + 'Videos': 0, + 'Others': { + data: {}, + count: 0, + type: 'detailed' + }, + 'Temporary': 0, + 'Size in DB': { + value: 0, + type: 'byte' + } + }; + + const getFilesCountAndSize = async () => { + const uploads = await db.table('files').select('size'); + + return { + 'Total': uploads.length, + 'Size in DB': { + value: uploads.reduce((acc, upload) => acc + parseInt(upload.size, 10), 0), + type: 'byte' + } + }; + }; + + const getImagesCount = async () => { + const Images = await db.table('files') + .where('type', 'like', `image/%`) + .count('id as count') + .then(rows => rows[0].count); + + return { Images }; + }; + + const getVideosCount = async () => { + const Videos = await db.table('files') + .where('type', 'like', `video/%`) + .count('id as count') + .then(rows => rows[0].count); + + return { Videos }; + }; + + const getOthersCount = async () => { + // rename to key, value from type, count + const data = await db.table('files') + .select('type as key') + .count('id as value') + .whereNot('type', 'like', `image/%`) + .whereNot('type', 'like', `video/%`) + .groupBy('key') + .orderBy('value', 'desc'); + + const count = data.reduce((acc, val) => acc + val.value, 0); + + return { + Others: { + data, + count, + type: 'detailed' + } + }; + }; + + const result = await Promise.all([getFilesCountAndSize(), getImagesCount(), getVideosCount(), getOthersCount()]); + + return { ...stats, ...Object.assign({}, ...result) }; + } + + async getUsersInfo(db) { + const stats = { + Total: 0, + Admins: 0, + Disabled: 0 + }; + + const users = await db.table('users'); + stats.Total = users.length; + + for (const user of users) { + if (!user.enabled) { + stats.Disabled++; + } + + if (user.isAdmin) { + stats.Admins++; + } + } + + return stats; + } + + async getAlbumStats(db) { + const stats = { + 'Total': 0, + 'NSFW': 0, + 'Generated archives': 0, + 'Generated identifiers': 0, + 'Files in albums': 0 + }; + + const albums = await db.table('albums'); + stats.Total = albums.length; + for (const album of albums) { + if (album.nsfw) stats.NSFW++; + if (album.zipGeneratedAt) stats['Generated archives']++; // XXX: Bobby checks each after if a zip really exists on the disk. Is it really needed? + } + + stats['Generated identifiers'] = await db.table('albumsLinks').count('id as count').then(rows => rows[0].count); + stats['Files in albums'] = await db.table('albumsFiles') + .whereNotNull('albumId') + .count('id as count') + .then(rows => rows[0].count); + + return stats; + } + + async run(req, res, db) { + const tmp = { + system: await this.getSystemInfo(), + fileSystems: await this.getFileSystemsInfo(), + uploads: await this.getUploadsInfo(db), + users: await this.getUsersInfo(db), + albums: await this.getAlbumStats(db) + }; + + return res.json(tmp); + } +} + +module.exports = filesGET; |