From 0da1dab95db7aec8da119fb5d503877793b7e41e Mon Sep 17 00:00:00 2001
From: mrfry <mr.fry@tutanota.com>
Date: Tue, 4 May 2021 17:59:15 +0200
Subject: [PATCH] Added user file vote api

---
 src/modules/api/api.ts                  |  30 ++--
 src/modules/api/submodules/userFiles.ts | 195 ++++++++++++++++++------
 2 files changed, 166 insertions(+), 59 deletions(-)

diff --git a/src/modules/api/api.ts b/src/modules/api/api.ts
index 4a854d2..eed8e73 100644
--- a/src/modules/api/api.ts
+++ b/src/modules/api/api.ts
@@ -54,6 +54,8 @@ function GetApp(): ModuleType {
   domain = domain.join('.') // "frylabs.net"
   logger.DebugLog(`Cookie domain: ${domain}`, 'cookie', 1)
 
+  // -------------------------------------------------------------------------------------------
+
   app.use(
     bodyParser.urlencoded({
       limit: '10mb',
@@ -81,15 +83,7 @@ function GetApp(): ModuleType {
       ],
     })
   )
-  publicdirs.forEach((pdir) => {
-    logger.Log(`Using public dir: ${pdir}`)
-    app.use(express.static(pdir))
-  })
-  app.use(
-    fileUpload({
-      limits: { fileSize: 50 * 1024 * 1024 },
-    })
-  )
+  // -------------------------------------------------------------------------------------------
 
   let rootRedirectURL = ''
 
@@ -128,7 +122,7 @@ function GetApp(): ModuleType {
 
   // --------------------------------------------------------------
 
-  app.get('/', function(req: Request, res: any) {
+  app.get('/', function (req: Request, res: any) {
     logger.LogReq(req)
     if (reloadRootRedirectURL) {
       res.redirect(rootRedirectURL)
@@ -143,11 +137,23 @@ function GetApp(): ModuleType {
 
   // -------------------------------------------------------------------------------------------
 
-  app.get('*', function(req: Request, res: any) {
+  publicdirs.forEach((pdir) => {
+    logger.Log(`Using public dir: ${pdir}`)
+    app.use(express.static(pdir))
+  })
+  app.use(
+    fileUpload({
+      limits: { fileSize: 50 * 1024 * 1024 },
+    })
+  )
+
+  // -------------------------------------------------------------------------------------------
+
+  app.get('*', function (req: Request, res: any) {
     res.status(404).render('404')
   })
 
-  app.post('*', function(req: Request, res: any) {
+  app.post('*', function (req: Request, res: any) {
     res.status(404).render('404')
   })
 
diff --git a/src/modules/api/submodules/userFiles.ts b/src/modules/api/submodules/userFiles.ts
index d2f1029..34e66b0 100644
--- a/src/modules/api/submodules/userFiles.ts
+++ b/src/modules/api/submodules/userFiles.ts
@@ -4,11 +4,102 @@ import logger from '../../../utils/logger'
 import utils from '../../../utils/utils'
 import { Request, SubmoduleData, User } from '../../../types/basicTypes'
 
-const usersFileName = '.users.json'
+const dataFileName = '.data.json'
+
+function listDir(publicDir, subdir, userFilesDir) {
+  const safeSubdir = subdir.replace(/\.+/g, '').replace(/\/+/g, '')
+  const dir = userFilesDir + '/' + safeSubdir
+  const usersFile = dir + '/' + dataFileName
+
+  if (!utils.FileExists(dir)) {
+    return {
+      success: false,
+      msg: `Directory ${subdir} does not exists`,
+    }
+    return
+  }
+  if (!utils.FileExists(usersFile)) {
+    utils.WriteFile('{}', usersFile)
+  }
+  const users = utils.ReadJSON(usersFile)
+
+  if (!utils.FileExists(dir)) {
+    return {
+      success: false,
+      msg: `Path '${safeSubdir}' does not exists`,
+    }
+    return
+  }
+
+  return {
+    success: true,
+    files: utils.ReadDir(dir).reduce((acc, file) => {
+      const stat = fs.lstatSync(dir + '/' + file)
+
+      if (stat.isDirectory()) {
+        return acc
+      }
+
+      acc.push({
+        name: file,
+        path: dir.replace(publicDir, '') + '/' + file,
+        size: stat.size,
+        date: stat.mtime.getTime(),
+        user: users && users[file] ? users[file].uid : -1,
+        views:
+          users && users[file] && users[file].views ? users[file].views : 0,
+        upvotes:
+          users && users[file] && users[file].upvotes
+            ? users[file].upvotes
+            : [],
+        downvotes:
+          users && users[file] && users[file].downvotes
+            ? users[file].downvotes
+            : [],
+      })
+      return acc
+    }, []),
+  }
+}
 
 function setup(data: SubmoduleData): void {
   const { app, /* userDB, url, */ publicdirs /* moduleSpecificData */ } = data
 
+  app.use((req: Request, res, next) => {
+    // /userFiles/test/2021-04-28_10-59.png
+    try {
+      if (req.url.includes('/userFiles/')) {
+        logger.LogReq(req)
+        const safePath = decodeURIComponent(req.url)
+          .split('?')[0]
+          .replace(/\.+/g, '.')
+          .replace(/\/+/g, '/')
+        const x = safePath.split('/')
+        const dir = x[2]
+        const fname = x.pop()
+        const dataFilePath = userFilesDir + '/' + dir + '/' + dataFileName
+
+        const data = utils.ReadJSON(dataFilePath)
+
+        if (data[fname]) {
+          if (!data[fname].views) {
+            data[fname].views = 0
+          }
+          data[fname].views = data[fname].views + 1
+
+          utils.WriteFile(JSON.stringify(data), dataFilePath)
+        }
+      }
+    } catch (e) {
+      console.error(e)
+      logger.Log(
+        `Error trying to update view count on ${req.url}`,
+        logger.GetColor('redbg')
+      )
+    }
+    next()
+  })
+
   const publicDir = publicdirs[0]
 
   const userFilesDir = publicDir + 'userFiles'
@@ -26,49 +117,8 @@ function setup(data: SubmoduleData): void {
     const subdir: any = req.query.subdir
 
     if (subdir) {
-      const safeSubdir = subdir.replace(/\.+/g, '').replace(/\/+/g, '')
-      const dir = userFilesDir + '/' + safeSubdir
-      const usersFile = dir + '/' + usersFileName
-
-      if (!utils.FileExists(dir)) {
-        res.json({
-          success: false,
-          msg: `Directory ${subdir} does not exists`,
-        })
-        return
-      }
-      if (!utils.FileExists(usersFile)) {
-        utils.WriteFile('{}', usersFile)
-      }
-      const users = utils.ReadJSON(usersFile)
-
-      if (!utils.FileExists(dir)) {
-        res.json({
-          success: false,
-          msg: `Path '${safeSubdir}' does not exists`,
-        })
-        return
-      }
-
-      res.json({
-        success: true,
-        files: utils.ReadDir(dir).reduce((acc, file) => {
-          const stat = fs.lstatSync(dir + '/' + file)
-
-          if (stat.isDirectory()) {
-            return acc
-          }
-
-          acc.push({
-            name: file,
-            path: dir.replace(publicDir, '') + '/' + file,
-            size: stat.size,
-            date: stat.mtime.getTime(),
-            user: users[file] || -1,
-          })
-          return acc
-        }, []),
-      })
+      const result = listDir(publicDir, subdir, userFilesDir)
+      res.json(result)
     } else {
       res.json({
         success: true,
@@ -113,7 +163,7 @@ function setup(data: SubmoduleData): void {
       return
     }
     utils.deleteFile(filePath)
-    const usersFile = userFilesDir + '/' + safeDir + '/' + usersFileName
+    const usersFile = userFilesDir + '/' + safeDir + '/' + dataFileName
     const users = utils.ReadJSON(usersFile)
     delete users[safeFname]
     utils.WriteFile(JSON.stringify(users), usersFile)
@@ -179,9 +229,9 @@ function setup(data: SubmoduleData): void {
           logger.GetColor('blue')
         )
 
-        const usersFile = userFilesDir + '/' + safeDir + '/' + usersFileName
+        const usersFile = userFilesDir + '/' + safeDir + '/' + dataFileName
         const users = utils.ReadJSON(usersFile)
-        users[body.fileName] = user.id
+        users[body.fileName] = { uid: user.id }
         utils.WriteFile(JSON.stringify(users), usersFile)
 
         res.json({
@@ -192,6 +242,57 @@ function setup(data: SubmoduleData): void {
         res.end('something bad happened :s')
       })
   })
+
+  app.post('/voteFile', (req: Request, res) => {
+    logger.LogReq(req)
+    const user: User = req.session.user
+    // { path: 'userFiles/test/2021-04-28_10-59.png', to: 'up' } 19
+    const { path, to } = req.body
+    const safePath = path.replace(/\.+/g, '.').replace(/\/+/g, '/')
+    const x = safePath.split('/')
+    const dir = x[1]
+    const fname = x.pop()
+    const dataFilePath = userFilesDir + '/' + dir + '/' + dataFileName
+
+    const data = utils.ReadJSON(dataFilePath)
+
+    if (data[fname]) {
+      if (!data[fname].upvotes) {
+        data[fname].upvotes = []
+      }
+      if (!data[fname].downvotes) {
+        data[fname].downvotes = []
+      }
+
+      const removeVote = (from, uid) => {
+        if (!from.includes(uid)) {
+          return from
+        }
+        return from.reduce((acc, id) => {
+          if (id !== uid) {
+            acc = [...acc, id]
+          }
+          return acc
+        }, [])
+      }
+
+      data[fname].downvotes = removeVote(data[fname].downvotes, user.id)
+      data[fname].upvotes = removeVote(data[fname].upvotes, user.id)
+
+      if (to === 'up') {
+        data[fname].upvotes = [...data[fname].upvotes, user.id]
+      } else if (to === 'down') {
+        data[fname].downvotes = [...data[fname].downvotes, user.id]
+      } else if (to === 'clear') {
+        // ... already cleared
+      }
+
+      utils.WriteFile(JSON.stringify(data), dataFilePath)
+    }
+
+    const result = listDir(publicDir, dir, userFilesDir)
+    res.json(result)
+  })
 }
 
 export default {