mirror of https://github.com/JustKato/FreePad.git
Implemented a views counter
+ Implemented views for the post struct + Views functions + Views storage file
This commit is contained in:
parent
23dd69e060
commit
53066025f0
|
@ -28,9 +28,23 @@ func TaskManager() {
|
|||
cleanupInterval = 1
|
||||
}
|
||||
|
||||
fmt.Println("[Task::Cleanup]: Task registered")
|
||||
for range time.Tick(time.Minute * 5) {
|
||||
objects.CleanupPosts(cleanupInterval)
|
||||
}
|
||||
// Run all handlers
|
||||
go cleanupHandler(cleanupInterval)
|
||||
go savePostHandler()
|
||||
|
||||
}
|
||||
|
||||
func savePostHandler() {
|
||||
// Save the views cache
|
||||
fmt.Println("[Task::Save]: File save registered")
|
||||
for range time.NewTicker(time.Second).C {
|
||||
objects.SavePostViewsCache()
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupHandler(cleanupInterval int) {
|
||||
fmt.Println("[Task::Cleanup]: Cleanup task registered")
|
||||
for range time.NewTicker(time.Minute * 5).C {
|
||||
objects.CleanupPosts(cleanupInterval)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,154 @@
|
|||
package objects
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/JustKato/FreePad/lib/helper"
|
||||
)
|
||||
|
||||
// Initialize the views cache
|
||||
var ViewsCache map[string]uint32 = make(map[string]uint32)
|
||||
|
||||
// Mutex lock for the ViewsCache
|
||||
var viewersLock sync.Mutex
|
||||
|
||||
type Post struct {
|
||||
Name string `json:"name"`
|
||||
LastModified string `json:"last_modified"`
|
||||
Content string `json:"content"`
|
||||
Views uint32 `json:"views"`
|
||||
}
|
||||
|
||||
// Get the path to the views JSON
|
||||
func getViewsFilePath() (string, error) {
|
||||
// Get the path to the storage then append the const name for the storage file
|
||||
filePath := path.Join(getStorageDirectory(), "views_storage.json")
|
||||
|
||||
// Check if the file exists
|
||||
if _, err := os.Stat(filePath); errors.Is(err, os.ErrNotExist) {
|
||||
// Create the file
|
||||
err := os.WriteFile(filePath, []byte(""), 0777)
|
||||
if err != nil {
|
||||
return ``, err
|
||||
}
|
||||
}
|
||||
|
||||
// Return the file path
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// Load the views cache from file
|
||||
func LoadViewsCache() error {
|
||||
// Get the views file path
|
||||
viewsFilePath, err := getViewsFilePath()
|
||||
if err != nil {
|
||||
// This is now a problem for the upstairs
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the contents of the file as a map
|
||||
f, err := os.ReadFile(viewsFilePath)
|
||||
if err != nil {
|
||||
// This is now a problem for the upstairs
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the contents are valid
|
||||
if len(f) <= 0 && len(string(f)) <= 0 {
|
||||
// The file is completely empty!
|
||||
return nil
|
||||
}
|
||||
|
||||
var parsedData map[string]uint32
|
||||
|
||||
// Parse the data
|
||||
err = json.Unmarshal(f, &parsedData)
|
||||
if err != nil {
|
||||
// This is now a problem for the function caller :D
|
||||
return err
|
||||
}
|
||||
|
||||
storageDir := getStorageDirectory()
|
||||
// Loop through all of the mapped files
|
||||
for fileName := range parsedData {
|
||||
// Grab the path to the file
|
||||
// TODO: Create a generic function that checks if a post exists by name to use here adn in the GetPost method.
|
||||
filePath := path.Join(storageDir, fileName)
|
||||
// Check if the file exists
|
||||
if _, err := os.Stat(filePath); !os.IsNotExist(err) {
|
||||
// Looks like the file does not exist anymore, remove it from the map
|
||||
delete(parsedData, fileName)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the current cache
|
||||
ViewsCache = parsedData
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddViewToPost(postName string) uint32 {
|
||||
// Lock the viewers mapping
|
||||
viewersLock.Lock()
|
||||
|
||||
// Check if the map has any value set to this elem
|
||||
if _, ok := ViewsCache[postName]; !ok {
|
||||
// Set the map value
|
||||
ViewsCache[postName] = 0
|
||||
}
|
||||
|
||||
// Add to the counter
|
||||
ViewsCache[postName]++
|
||||
|
||||
// Unlock
|
||||
viewersLock.Unlock()
|
||||
|
||||
// Return the value
|
||||
return ViewsCache[postName]
|
||||
}
|
||||
|
||||
func SavePostViewsCache() error {
|
||||
|
||||
data, err := json.Marshal(ViewsCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
viewsFilePath, err := getViewsFilePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(viewsFilePath, os.O_WRONLY|os.O_CREATE, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Actually close the file
|
||||
defer f.Close()
|
||||
|
||||
// Delete all past content
|
||||
err = f.Truncate(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Reset pointer
|
||||
_, err = f.Seek(0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write the json into storage
|
||||
_, err = f.Write(data)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the path to the storage directory
|
||||
|
@ -47,9 +182,13 @@ func GetPost(fileName string) Post {
|
|||
// Generate the file path
|
||||
filePath := fmt.Sprintf("%s%s", storageDir, fileName)
|
||||
|
||||
// Get the post views and add 1 to them
|
||||
postViews := AddViewToPost(fileName)
|
||||
|
||||
p := Post{
|
||||
Name: fileName,
|
||||
Content: "",
|
||||
Views: postViews,
|
||||
LastModified: "Never Before",
|
||||
}
|
||||
|
||||
|
@ -94,6 +233,8 @@ func WritePost(p Post) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Actually close the file
|
||||
defer f.Close()
|
||||
|
||||
// Write the contnets
|
||||
_, err = f.WriteString(p.Content)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
|
@ -23,6 +24,13 @@ func HomeRoutes(router *gin.Engine) {
|
|||
// Get the post we are looking for.
|
||||
postName := c.Param("post")
|
||||
|
||||
if postName == `views_storage.json` {
|
||||
// Redirect the user to the homepage as this is a reserved keyword
|
||||
c.Redirect(http.StatusPermanentRedirect, "/")
|
||||
// Do not proceed further
|
||||
return
|
||||
}
|
||||
|
||||
// Get the maximum pad size, so that we may notify the client-side to match server-side
|
||||
maximumPadSize := helper.GetMaximumPadSize()
|
||||
|
||||
|
@ -40,6 +48,7 @@ func HomeRoutes(router *gin.Engine) {
|
|||
"post_content": post.Content,
|
||||
"maximumPadSize": maximumPadSize,
|
||||
"last_modified": post.LastModified,
|
||||
"views": post.Views,
|
||||
"domain_base": helper.GetDomainBase(),
|
||||
})
|
||||
})
|
||||
|
@ -59,6 +68,7 @@ func HomeRoutes(router *gin.Engine) {
|
|||
p := objects.Post{
|
||||
Name: postName,
|
||||
Content: postContent,
|
||||
Views: 0, // This can just be ignored
|
||||
LastModified: time.Now().Format("02/01/2006 03:04:05 PM"),
|
||||
}
|
||||
|
||||
|
|
9
main.go
9
main.go
|
@ -1,9 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/JustKato/FreePad/lib/controllers"
|
||||
"github.com/JustKato/FreePad/lib/objects"
|
||||
"github.com/JustKato/FreePad/lib/routes"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/joho/godotenv"
|
||||
|
@ -22,6 +24,13 @@ func main() {
|
|||
// Run the TaskManager
|
||||
go controllers.TaskManager()
|
||||
|
||||
// Load in the views data from storage
|
||||
err := objects.LoadViewsCache()
|
||||
if err != nil {
|
||||
fmt.Println("Failed to load views from cache")
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// Initialize the router
|
||||
router := gin.Default()
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="text" class="form-control" readonly value="1">
|
||||
<input type="text" class="form-control" readonly value="{{.views}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in New Issue