mirror of https://github.com/JustKato/FreePad.git
Compare commits
5 Commits
d949b3decb
...
bf1d032e68
Author | SHA1 | Date |
---|---|---|
Daniel Legt | bf1d032e68 | |
Daniel Legt | faff1ab527 | |
Daniel Legt | d056a4d429 | |
Daniel Legt | b710d24a2d | |
Daniel Legt | c3c9aacac3 |
|
@ -1,8 +1,12 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/JustKato/FreePad/lib/helper"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
@ -14,6 +18,45 @@ func AdminMiddleware(router *gin.RouterGroup) {
|
|||
// Check which route we are accessing
|
||||
fmt.Println(`Accesing: `, ctx.Request.RequestURI)
|
||||
|
||||
// Check if the request is other than the login request
|
||||
if ctx.Request.RequestURI != "/admin/login" {
|
||||
// Check if the user is logged-in
|
||||
|
||||
fmt.Println(`Checking if admin`)
|
||||
|
||||
if !IsAdmin(ctx) {
|
||||
// Not an admin, redirect to homepage
|
||||
ctx.Redirect(http.StatusTemporaryRedirect, "/")
|
||||
ctx.Abort()
|
||||
|
||||
fmt.Println(`Not an admin!`)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func IsAdmin(ctx *gin.Context) bool {
|
||||
adminToken, err := ctx.Cookie("admin_token")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Encode the real token
|
||||
sha512Hasher := sha512.New()
|
||||
sha512Hasher.Write([]byte(helper.GetAdminToken()))
|
||||
hashHexToken := sha512Hasher.Sum(nil)
|
||||
trueToken := hex.EncodeToString(hashHexToken)
|
||||
|
||||
// Check if the user's admin token matches the token
|
||||
if adminToken != "" && adminToken == trueToken {
|
||||
// Yep, it's the admin!
|
||||
return true
|
||||
}
|
||||
|
||||
// Definitely not an admin
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -26,6 +26,13 @@ type Post struct {
|
|||
Views uint32 `json:"views"`
|
||||
}
|
||||
|
||||
func (p *Post) Delete() error {
|
||||
filePath := path.Join(getStorageDirectory(), p.Name)
|
||||
|
||||
// Remove the file and return the result
|
||||
return os.Remove(filePath)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -94,7 +101,7 @@ func LoadViewsCache() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func AddViewToPost(postName string) uint32 {
|
||||
func AddViewToPost(postName string, incrementViews bool) uint32 {
|
||||
// Lock the viewers mapping
|
||||
viewersLock.Lock()
|
||||
|
||||
|
@ -104,8 +111,10 @@ func AddViewToPost(postName string) uint32 {
|
|||
ViewsCache[postName] = 0
|
||||
}
|
||||
|
||||
if incrementViews {
|
||||
// Add to the counter
|
||||
ViewsCache[postName]++
|
||||
}
|
||||
|
||||
// Unlock
|
||||
viewersLock.Unlock()
|
||||
|
@ -175,7 +184,7 @@ func getStorageDirectory() string {
|
|||
}
|
||||
|
||||
// Get a post from the file system
|
||||
func GetPost(fileName string) Post {
|
||||
func GetPost(fileName string, incrementViews bool) Post {
|
||||
// Get the base storage directory and make sure it exists
|
||||
storageDir := getStorageDirectory()
|
||||
|
||||
|
@ -183,7 +192,7 @@ func GetPost(fileName string) Post {
|
|||
filePath := fmt.Sprintf("%s%s", storageDir, fileName)
|
||||
|
||||
// Get the post views and add 1 to them
|
||||
postViews := AddViewToPost(fileName)
|
||||
postViews := AddViewToPost(fileName, incrementViews)
|
||||
|
||||
p := Post{
|
||||
Name: fileName,
|
||||
|
@ -295,3 +304,30 @@ func CleanupPosts(age int) {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
func GetAllPosts() []Post {
|
||||
// Initialize the list of posts
|
||||
postList := []Post{}
|
||||
|
||||
// Get the posts storage directory
|
||||
storageDir := getStorageDirectory()
|
||||
|
||||
// Read the directory listing
|
||||
files, err := os.ReadDir(storageDir)
|
||||
// Check if thereh as been an issues with reading the directory contents
|
||||
if err != nil {
|
||||
// Log the error
|
||||
fmt.Println("Error::GetAllPosts:", err)
|
||||
// Return an empty list to have a clean fallback
|
||||
return []Post{}
|
||||
}
|
||||
|
||||
// Go through all of the files
|
||||
for _, v := range files {
|
||||
// Process the file into a pad
|
||||
postList = append(postList, GetPost(v.Name(), false))
|
||||
}
|
||||
|
||||
// Return the post list
|
||||
return postList
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/JustKato/FreePad/lib/controllers"
|
||||
"github.com/JustKato/FreePad/lib/helper"
|
||||
"github.com/JustKato/FreePad/lib/objects"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"crypto/sha512"
|
||||
|
@ -44,37 +45,51 @@ func AdminRoutes(router *gin.RouterGroup) {
|
|||
hashHexToken := sha512Hasher.Sum(nil)
|
||||
hashToken := hex.EncodeToString(hashHexToken)
|
||||
|
||||
fmt.Println(hashToken)
|
||||
|
||||
// Set the cookie
|
||||
ctx.SetCookie("admin_token", hashToken, 60*60, "/", helper.GetDomainBase(), true, true)
|
||||
|
||||
ctx.Request.Method = "GET"
|
||||
|
||||
// Redirect the user to the admin page
|
||||
ctx.Redirect(http.StatusTemporaryRedirect, "/admin")
|
||||
ctx.Redirect(http.StatusFound, "/admin/view")
|
||||
return
|
||||
} else {
|
||||
ctx.Request.Method = "GET"
|
||||
|
||||
// Redirect the user to the admin page
|
||||
ctx.Redirect(http.StatusTemporaryRedirect, "/admin/login?fail")
|
||||
ctx.Redirect(http.StatusFound, "/admin/login?fail")
|
||||
return
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
router.GET("/delete/:padname", func(ctx *gin.Context) {
|
||||
// Get the pad name that we bout' to delete
|
||||
padName := ctx.Param("padname")
|
||||
|
||||
// Try and get the pad, check if valid
|
||||
pad := objects.GetPost(padName, false)
|
||||
|
||||
// Delete the pad
|
||||
err := pad.Delete()
|
||||
fmt.Println(err)
|
||||
|
||||
// Redirect the user to the admin page
|
||||
ctx.Redirect(http.StatusFound, "/admin/view")
|
||||
})
|
||||
|
||||
// Admin view route
|
||||
router.GET("/", func(ctx *gin.Context) {
|
||||
router.GET("/view", func(ctx *gin.Context) {
|
||||
|
||||
adminToken, err := ctx.Cookie("admin_token")
|
||||
if err != nil {
|
||||
adminToken = ""
|
||||
}
|
||||
// Get all of the pads as a listing
|
||||
padList := objects.GetAllPosts()
|
||||
|
||||
ctx.JSON(200, gin.H{
|
||||
`adminToken`: adminToken,
|
||||
ctx.HTML(200, "admin_view.html", gin.H{
|
||||
"title": "Admin",
|
||||
"padList": padList,
|
||||
"domain_base": helper.GetDomainBase(),
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func HomeRoutes(router *gin.Engine) {
|
|||
}
|
||||
postName = sanitize.XSS(sanitize.SingleLine(postName))
|
||||
|
||||
post := objects.GetPost(postName)
|
||||
post := objects.GetPost(postName, true)
|
||||
|
||||
c.HTML(200, "page.html", gin.H{
|
||||
"title": postName,
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
{{ template "inc/header.html" .}}
|
||||
|
||||
<style>
|
||||
|
||||
.pad-instance {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#pad-list {
|
||||
max-height: 30rem;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pad-name {
|
||||
max-width: 30%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
||||
<main id="main-card" class="container rounded mt-5 shadow-sm">
|
||||
<div class="p-3">
|
||||
|
||||
<a href="/" class="logo-container w-100 d-flex mb-4">
|
||||
<img src="/static/img/logo_transparent.png" alt="Logo" style="max-width: 50%; margin: 0 auto;" class="mx-auto">
|
||||
</a>
|
||||
|
||||
<div class="form-group my-4 border-top p-3 border">
|
||||
|
||||
<div class="pad-instance my-2 border-bottom">
|
||||
<div class="pad-name col-5">
|
||||
Pad Name
|
||||
</div>
|
||||
<div class="pad-last-view col-1">
|
||||
Views
|
||||
</div>
|
||||
<div class="pad-last-modified col-4">
|
||||
Create Date
|
||||
</div>
|
||||
<div class="col-2">
|
||||
Actions
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pad-list" >
|
||||
{{ range $indx, $element := .padList }}
|
||||
|
||||
<div class="pad-instance my-2">
|
||||
<div class="pad-name col-5">
|
||||
<a href="/{{ $element.Name }}">
|
||||
{{ $element.Name }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="pad-last-view col-1">
|
||||
{{ $element.Views }}
|
||||
</div>
|
||||
<div class="pad-last-modified col-4">
|
||||
{{ $element.LastModified }}
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<div onclick="doDelete({{ $element.Name }})" class="btn btn-danger">
|
||||
Delete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
{{ template "inc/theme-toggle.html" .}}
|
||||
</body>
|
||||
|
||||
<script>
|
||||
function doDelete(id) {
|
||||
// Confirm deletion
|
||||
if ( confirm("Confirm pad deletion?") ) {
|
||||
// Do delete
|
||||
window.location.href = `/admin/delete/${id}`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{{ template "inc/footer.html" .}}
|
Loading…
Reference in New Issue