+ Handle post data

This commit is contained in:
Daniel Legt 2022-05-20 01:40:21 +03:00
parent a76b9d02ba
commit 051a8a311c
8 changed files with 154 additions and 27 deletions

View File

@ -14,4 +14,7 @@ PAD_STORAGE_PATH=./data/
API_BAN_LIMIT=300
# Wether or not to run it all in dev-mode
DEV_MODE=0
DEV_MODE=0
# Maximum file storage age in minutes
CLEANUP_MAX_AGE=1

2
.gitignore vendored
View File

@ -2,4 +2,4 @@ dev/*
!dev/.keep
.env
docker-compose.yaml
data/Hello World
data

2
go.mod
View File

@ -5,5 +5,5 @@ go 1.15
require (
github.com/gin-gonic/gin v1.7.7
github.com/joho/godotenv v1.4.0
github.com/mrz1836/go-sanitize v1.1.5 // indirect
github.com/mrz1836/go-sanitize v1.1.5
)

3
go.sum
View File

@ -34,8 +34,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@ -57,4 +57,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -6,8 +6,9 @@ import (
)
type Post struct {
Name string `json:"name"`
Content string `json:"content"`
Name string `json:"name"`
LastModified string `json:"last_modified"`
Content string `json:"content"`
}
func getStorageDirectory() string {
@ -39,16 +40,14 @@ func GetPost(fileName string) Post {
// Generate the file path
filePath := fmt.Sprintf("%s%s", storageDir, fileName)
fmt.Println("Reading: ", filePath)
p := Post{
Name: fileName,
Content: "",
Name: fileName,
Content: "",
LastModified: "Never Before",
}
// Check if the file exits
if _, err := os.Stat(filePath); !os.IsNotExist(err) {
fmt.Println("Found ", filePath)
// File does exist, read it and set the content
data, err := os.ReadFile(filePath)
if err != nil {
@ -57,7 +56,14 @@ func GetPost(fileName string) Post {
// Get the content of the file and put it in the response
p.Content = string(data)
fmt.Println("Loaded content for ", filePath)
// Get last modified date
fileData, err := os.Stat(filePath)
if err == nil {
p.LastModified = fileData.ModTime().Format("02/01/2006 03:04:05 PM")
} else {
fmt.Println(err)
}
}
return p
@ -65,5 +71,26 @@ func GetPost(fileName string) Post {
func WritePost(p Post) error {
// Get the base storage directory and make sure it exists
storageDir := getStorageDirectory()
// Generate the file path
filePath := fmt.Sprintf("%s%s", storageDir, p.Name)
f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return err
}
// Write the contnets
_, err = f.WriteString(p.Content)
if err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
return nil
}

View File

@ -1,8 +1,8 @@
package routes
import (
"fmt"
"net/url"
"time"
"github.com/JustKato/FreePad/lib/helper"
"github.com/JustKato/FreePad/lib/objects"
@ -23,7 +23,27 @@ func HomeRoutes(router *gin.Engine) {
// Get the post we are looking for.
postName := c.Param("post")
fmt.Println("Sanitizing ", postName)
// Sanitize the postName
newPostName, err := url.QueryUnescape(postName)
if err == nil {
postName = newPostName
}
postName = sanitize.AlphaNumeric(postName, true)
post := objects.GetPost(postName)
c.HTML(200, "page.html", gin.H{
"title": postName,
"post_content": post.Content,
"last_modified": post.LastModified,
"domain_base": helper.GetDomainBase(),
})
})
router.POST("/:post", func(c *gin.Context) {
// Get the post we are looking for.
postName := c.Param("post")
postContent := c.PostForm("content")
// Sanitize the postName
newPostName, err := url.QueryUnescape(postName)
@ -32,14 +52,26 @@ func HomeRoutes(router *gin.Engine) {
}
postName = sanitize.AlphaNumeric(postName, true)
fmt.Println("Fetching ", postName)
p := objects.Post{
Name: postName,
Content: postContent,
LastModified: time.Now().Format("02/01/2006 03:04:05 PM"),
}
post := objects.GetPost(postName)
// Write the post
err = objects.WritePost(p)
if err != nil {
c.JSON(400, gin.H{
"error": err,
})
c.HTML(200, "page.html", gin.H{
"title": postName,
"post_content": post.Content,
"domain_base": helper.GetDomainBase(),
// End
return
}
// Return the success message
c.JSON(200, gin.H{
"pad": p,
})
})

View File

@ -13,8 +13,8 @@ func main() {
// Load environment variables, ignore if any errors come up
godotenv.Load()
_, isDevelopment := os.LookupEnv("DEV_MODE")
if isDevelopment {
dm, isDevelopment := os.LookupEnv("DEV_MODE")
if !isDevelopment && dm == "0" {
gin.SetMode(gin.ReleaseMode)
}

View File

@ -17,11 +17,21 @@
</div>
<textarea name="pad-content" id="pad-content"
class="form-control">{{.post_content}}</textarea>
<textarea name="pad-content" id="pad-content" onchange="sendMyData(this)" onkeydown="updateStatus(`Not Saved`, `text-danger`)" class="form-control">{{.post_content}}</textarea>
<div id="pad-status" class="my-4 row">
<div class="col-md-12 col-lg-6 col-xl-6" title="Current Viewers">
<div class="col-md-12 col-lg-4 col-xl-4" title="Status">
<div class="input-group">
<span class="input-group-text">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-reception-3" viewBox="0 0 16 16">
<path d="M0 11.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-8zm4 8a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5z"/>
</svg>
</span>
<input type="text" class="form-control" readonly value="Loaded" id="loading_status">
</div>
</div>
<div class="col-md-12 col-lg-4 col-xl-4 mt-4 mt-lg-0 mt-xl-0" title="Current Viewers">
<div class="input-group">
<span class="input-group-text">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
@ -33,14 +43,14 @@
</div>
</div>
<div class="col-md-12 col-lg-6 col-xl-6 mt-4 mt-lg-0 mt-xl-0" title="Last Modified">
<div class="col-md-12 col-lg-4 col-xl-4 mt-4 mt-lg-0 mt-xl-0" title="Last Modified">
<div class="input-group">
<span class="input-group-text">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-hourglass-split" viewBox="0 0 16 16">
<path d="M2.5 15a.5.5 0 1 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1 0-1h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11zm2-13v1c0 .537.12 1.045.337 1.5h6.326c.216-.455.337-.963.337-1.5V2h-7zm3 6.35c0 .701-.478 1.236-1.011 1.492A3.5 3.5 0 0 0 4.5 13s.866-1.299 3-1.48V8.35zm1 0v3.17c2.134.181 3 1.48 3 1.48a3.5 3.5 0 0 0-1.989-3.158C8.978 9.586 8.5 9.052 8.5 8.351z"/>
</svg>
</span>
<input type="text" class="form-control" readonly value="Never Before">
<input type="text" class="form-control" id="last_modified_" readonly value="{{.last_modified}}">
</div>
</div>
@ -60,4 +70,58 @@
{{ template "inc/theme-toggle.html" .}}
</body>
<script>
function sendMyData(el) {
const formData = new FormData();
el.setAttribute(`readonly`, `1`);
formData.set("content", el.value);
updateStatus(`Attempting to save...`, `text-warning`);
fetch(window.location.href.toString(), {
body: formData,
method: "post",
})
.then( resp => {
console.log(resp);
resp.json()
.then( e => {
console.log(e.pad);
document.getElementById(`last_modified_`).value = e.pad.last_modified;
updateStatus(`Succesfully Saved`, `text-success`);
})
.catch( err => {
console.error(err);
updateStatus(`Failed to Save`, `text-error`);
})
})
.catch( err => {
console.error(err);
updateStatus(`Failed to Save`, `text-error`);
})
.finally( () => {
el.removeAttribute(`readonly`);
})
}
function updateStatus(txt, cls) {
const loading_status = document.getElementById(`loading_status`)
loading_status.value = txt;
loading_status.classList.remove("text-danger", "text-warning", "text-success", "text-white", "text-primary");
loading_status.classList.add(cls);
}
document.addEventListener(`DOMContentLoaded`, e => {
document.getElementById(`pad-content`).focus();
})
</script>
{{ template "inc/footer.html" .}}