Refactor de subidas y eliminaciones

This commit is contained in:
danoloan 2022-02-16 23:59:19 +01:00
parent b110cdcacc
commit b37dd824de
7 changed files with 243 additions and 96 deletions

62
main.go
View File

@ -1,6 +1,8 @@
package main
import (
"crypto/sha512"
"encoding/hex"
"html/template"
"igar/storage"
"io"
@ -9,6 +11,7 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
//"github.com/labstack/echo/middleware"
)
@ -56,6 +59,53 @@ func ViewController(c echo.Context) (err error) {
return
}
func AdminController(c echo.Context) (err error) {
entries, err := list.GetEntries()
if err == nil {
err = c.Render(http.StatusOK, "admin.html", entries)
}
return
}
func PostController(c echo.Context) (err error) {
desc := c.FormValue("desc")
date := c.FormValue("date")
file, err := c.FormFile("file")
if err != nil {
return
}
entry := storage.NewEntry(desc, date)
entry.GeneratePostID()
entry.AddMedia(file)
list.AddEntry(entry)
return c.String(http.StatusCreated, "")
}
func DeleteController(c echo.Context) (err error) {
uuid := c.QueryParam("uuid")
entry := list.RemoveEntry(uuid)
entry.RemoveFiles()
if entry == nil {
return c.String(http.StatusNotFound, "")
}
return c.String(http.StatusOK, "")
}
func auth(username, password string, c echo.Context) (bool, error) {
adminUsername := "danolo"
adminPassword := "bd4cad796950f50352225de3c773d8f3c39622bc17f34ad661eabe615cdf6d32751c5751e0648dc17d890f40330018334a2ae899878f200f6dc80121ddb70cc9"
hash := sha512.Sum512([]byte(password))
hashString := hex.EncodeToString(hash[:])
if username == adminUsername && hashString == adminPassword {
return true, nil
}
return false, nil
}
func main() {
e := echo.New()
@ -66,8 +116,6 @@ func main() {
e.GET("/", IndexController)
e.GET("/view", ViewController)
//e.GET("/list", server.ListController)
//e.GET("/rss", server.RSSController)
//e.GET("/json", server.JSONController)
//e.GET("/atom", server.AtomController)
@ -75,13 +123,13 @@ func main() {
e.Static("/static", "static")
e.Static("/img", "/var/lib/igar")
//admin := e.Group("/admin", middleware.BasicAuth(auth))
admin := e.Group("/admin", middleware.BasicAuth(auth))
//admin.GET("/", server.AdminController)
//admin.POST("/post", server.PostController)
//admin.DELETE("/post", DeleteController)
admin.GET("/", AdminController)
admin.POST("/post", PostController)
admin.DELETE("/post", DeleteController)
//admin.Static("/static", "admin/static")
admin.Static("/static", "admin/static")
e.Logger.Fatal(e.Start(":40404"))
}

View File

@ -2,20 +2,18 @@ package storage
import (
"fmt"
"io"
"mime"
"mime/multipart"
"os"
"os/exec"
"strings"
"time"
"github.com/google/uuid"
)
type (
JSTime time.Time
Media struct {
Type string `json:"type"`
File string `json:"file"`
Height int `json:"height"`
Width int `json:"width"`
}
Entry struct {
Date JSTime `json:"date"`
Description string `json:"description"`
@ -29,37 +27,6 @@ type (
}
)
const (
jsTimeLite = "2006-01-02"
jsTimeLayout = "2006-01-02 15:04:05"
adminUsername = "danolo"
adminPassword = "bd4cad796950f50352225de3c773d8f3c39622bc17f34ad661eabe615cdf6d32751c5751e0648dc17d890f40330018334a2ae899878f200f6dc80121ddb70cc9"
)
// JSTime
func (ct *JSTime) UnmarshalJSON(b []byte) (err error) {
s := strings.Trim(string(b), `"`)
nt, err := time.Parse(jsTimeLayout, s)
*ct = JSTime(nt)
return
}
func (ct JSTime) MarshalJSON() ([]byte, error) {
return []byte(ct.String()), nil
}
func (ct JSTime) String() string {
t := time.Time(ct)
return fmt.Sprintf("%q", t.Format(jsTimeLayout))
}
func (ct JSTime) Unix() int64 {
return time.Time(ct).Unix()
}
// Entry
func (entry Entry) Count() map[string]int {
count := make(map[string]int)
for _, media := range entry.Media {
@ -69,3 +36,77 @@ func (entry Entry) Count() map[string]int {
}
func (entry Entry) Len() int { return len(entry.Media) }
func (entry *Entry) GeneratePostID() {
entry.Post = strings.Replace(uuid.New().String(), "-", "", -1)
}
func (entry *Entry) AddMedia(file *multipart.FileHeader) (err error) {
src, err := file.Open()
if err != nil {
return
}
defer src.Close()
name := strings.Replace(uuid.New().String(), "-", "", -1)
extension, err := mime.ExtensionsByType(file.Header.Get("Content-Type"))
if err == nil {
name = fmt.Sprintf("%s%s", name, extension[0])
}
media := Media{
Type: "GraphImage",
File: name,
}
// TODO ruta relativa
postdir := fmt.Sprintf("/var/lib/igar/%s", entry.Post)
filepath := fmt.Sprintf("%s/%s", postdir, media.File)
os.MkdirAll(postdir, os.ModePerm)
dst, err := os.Create(filepath)
if err != nil {
return
}
defer dst.Close()
if _, err = io.Copy(dst, src); err != nil {
return
}
if err = exec.Command("node", "thumbs/make_thumb.js", postdir).Run(); err != nil {
// TODO fichero copiado no usado (limpieza?)
return
}
entry.Media = append(entry.Media, media)
return
}
func (entry *Entry) RemoveFiles() error {
// Beware of removing the root directory!
if entry.Post != "" {
path := fmt.Sprintf("/var/lib/igar/%s", entry.Post)
return os.RemoveAll(path)
}
// TODO return nil if not found?
return nil
}
func NewEntry(desc string, date string) (entry *Entry) {
var entryTime time.Time
if date != "" {
entryTime, _ = time.Parse(jsTimeLite, date)
} else {
entryTime = time.Now()
}
entry = &Entry{
Description: desc,
Date: JSTime(entryTime),
}
entry.GeneratePostID()
return
}

View File

@ -1,8 +1,6 @@
package storage
import (
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
@ -12,7 +10,6 @@ import (
"time"
"github.com/gorilla/feeds"
"github.com/labstack/echo"
)
type (
@ -26,7 +23,13 @@ type (
entryList []*Entry
)
func (list *EntryMap) Len() int { return len(list.mapped) }
// entryList
func (list entryList) Len() int { return len(list) }
func (list entryList) Swap(i, j int) { list[i], list[j] = list[j], list[i] }
func (list entryList) Less(i, j int) bool {
return time.Time(list[i].Date).After(time.Time(list[j].Date))
}
func (list *EntryMap) modified() (mod bool) {
info, err := os.Stat(list.Filename)
@ -76,45 +79,34 @@ func (list *EntryMap) read() (err error) {
return
}
func (list *EntryMap) GetEntries() (result []*Entry, err error) {
func (list *EntryMap) GetEntry(uuid string) (result Entry, err error) {
list.mu.Lock()
defer list.mu.Unlock()
if err = list.read(); err == nil {
result = *list.mapped[uuid]
}
return
}
func (list *EntryMap) GetEntries() (result []Entry, err error) {
list.mu.RLock()
defer list.mu.RUnlock()
err = list.read()
if err == nil {
result = list.sorted
result = make([]Entry, len(list.sorted))
for index, elem := range list.sorted {
result[index] = *elem
}
}
return
}
func (list *EntryMap) addEntry(entry *Entry) (err error) {
list.mu.Lock()
defer list.mu.Unlock()
list.read()
if entry != nil {
list.mapped[entry.Post] = entry
err = list.write()
}
return
}
func (list *EntryMap) GetEntry(uuid string) (result *Entry, err error) {
list.mu.Lock()
defer list.mu.Unlock()
if err = list.read(); err == nil {
result = list.mapped[uuid]
}
return
}
func (list *EntryMap) getFeed() (feed *feeds.Feed) {
func (list *EntryMap) GetFeed() (feed *feeds.Feed) {
feed = &feeds.Feed{
Title: "danoloan.es igar",
Link: &feeds.Link{Href: "https://danoloan.es/igar/"},
@ -149,19 +141,34 @@ func (list *EntryMap) getFeed() (feed *feeds.Feed) {
return
}
// entryList
func (list *EntryMap) Len() int { return len(list.mapped) }
func (list entryList) Len() int { return len(list) }
func (list entryList) Swap(i, j int) { list[i], list[j] = list[j], list[i] }
func (list entryList) Less(i, j int) bool {
return time.Time(list[i].Date).After(time.Time(list[j].Date))
}
func (list *EntryMap) AddEntry(entry *Entry) (err error) {
list.mu.Lock()
defer list.mu.Unlock()
func auth(username, password string, c echo.Context) (bool, error) {
hash := sha512.Sum512([]byte(password))
hashString := hex.EncodeToString(hash[:])
if username == adminUsername && hashString == adminPassword {
return true, nil
list.read()
if entry != nil {
list.mapped[entry.Post] = entry
err = list.write()
}
return false, nil
return
}
func (list *EntryMap) RemoveEntry(uuid string) (entry *Entry) {
list.mu.Lock()
defer list.mu.Unlock()
list.read()
entry, ok := list.mapped[uuid]
if ok {
delete(list.mapped, uuid)
}
list.write()
return
}

41
storage/jstime.go Normal file
View File

@ -0,0 +1,41 @@
package storage
import (
"fmt"
"strings"
"time"
)
type (
JSTime time.Time
)
const (
jsTimeLite = "2006-01-02"
jsTimeLayout = "2006-01-02 15:04:05"
)
func (ct *JSTime) UnmarshalJSON(b []byte) (err error) {
s := strings.Trim(string(b), `"`)
loc, _ := time.LoadLocation("Europe/Madrid")
nt, err := time.ParseInLocation(jsTimeLayout, s, loc)
*ct = JSTime(nt)
return
}
func (ct JSTime) MarshalJSON() ([]byte, error) {
return []byte(ct.String()), nil
}
func (ct JSTime) String() string {
t := time.Time(ct)
return fmt.Sprintf("%q", t.Format(jsTimeLayout))
}
func (ct JSTime) Unix() int64 {
return time.Time(ct).Unix()
}
func (ct JSTime) Local() JSTime {
return JSTime(time.Time(ct).Local())
}

10
storage/media.go Normal file
View File

@ -0,0 +1,10 @@
package storage
type (
Media struct {
Type string `json:"type"`
File string `json:"file"`
Height int `json:"height"`
Width int `json:"width"`
}
)

View File

@ -24,7 +24,7 @@
<button class="cajetin">Archivar/Eliminar imagen</button>
<div class="action list">
<div class="scroll" id="dellist">
{{ range .List }}
{{ range . }}
<div class="action">
<div class="date"> {{ .Date }} </div>
<div class="desc"> {{ .Description }} </div>
@ -68,7 +68,7 @@ function open(uuid) {
async function archive(uuid) {
if (confirm('Vas a archivar una imagen. ¿Estás seguro?')) {
response = await fetch(`post/${uuid}`, { method: 'DELETE' })
response = await fetch(`post?uuid=${uuid}`, { method: 'DELETE' })
if (response.ok) {
remove(this);
result(this, 'Imagen archivada');
@ -81,7 +81,7 @@ async function archive(uuid) {
async function delpost(uuid) {
if (confirm('Vas a eliminar una imagen. ¿Estás seguro?')) {
response = await fetch(`post/${uuid}?purge=true`, { method: 'DELETE' });
response = await fetch(`post?uuid=${uuid}&purge=true`, { method: 'DELETE' });
if (response.ok) {
remove(this);
result(this, 'Imagen eliminada');
@ -141,7 +141,7 @@ form.onsubmit = function(event) {
xhr.open('POST', 'post', true);
xhr.upload.onprogress = (prog) => {
result(form, `${prog.loaded / prog.total * 100}%`);
result(form, `${Math.round(prog.loaded * 100.0 / prog.total)}%`);
}
// Set up a handler for when the task for the request is complete

View File

@ -75,7 +75,7 @@ span.count {
{{ .Description }}
</div>
<div id="date">
{{ .Date.String }}
{{ .Date }}
</div>
<script>
;