From b37dd824de4061ebd0f100188af230aa787c5edc Mon Sep 17 00:00:00 2001 From: danoloan Date: Wed, 16 Feb 2022 23:59:19 +0100 Subject: [PATCH] Refactor de subidas y eliminaciones --- main.go | 62 ++++++++++++++-- storage/entry.go | 121 +++++++++++++++++++++---------- storage/{list.go => entrymap.go} | 95 +++++++++++++----------- storage/jstime.go | 41 +++++++++++ storage/media.go | 10 +++ templates/admin.html | 8 +- templates/view.html | 2 +- 7 files changed, 243 insertions(+), 96 deletions(-) rename storage/{list.go => entrymap.go} (83%) create mode 100644 storage/jstime.go create mode 100644 storage/media.go diff --git a/main.go b/main.go index 3818518..569951e 100644 --- a/main.go +++ b/main.go @@ -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")) } diff --git a/storage/entry.go b/storage/entry.go index 67371c7..12d81ee 100644 --- a/storage/entry.go +++ b/storage/entry.go @@ -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 +} diff --git a/storage/list.go b/storage/entrymap.go similarity index 83% rename from storage/list.go rename to storage/entrymap.go index d4919bf..d14977d 100644 --- a/storage/list.go +++ b/storage/entrymap.go @@ -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 } diff --git a/storage/jstime.go b/storage/jstime.go new file mode 100644 index 0000000..3f6aefc --- /dev/null +++ b/storage/jstime.go @@ -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()) +} diff --git a/storage/media.go b/storage/media.go new file mode 100644 index 0000000..10ddcca --- /dev/null +++ b/storage/media.go @@ -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"` + } +) diff --git a/templates/admin.html b/templates/admin.html index e62ee0c..de8058a 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -24,7 +24,7 @@
- {{ range .List }} + {{ range . }}
{{ .Date }}
{{ .Description }}
@@ -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 diff --git a/templates/view.html b/templates/view.html index f558cc1..376b7a6 100644 --- a/templates/view.html +++ b/templates/view.html @@ -75,7 +75,7 @@ span.count { {{ .Description }}
- {{ .Date.String }} + {{ .Date }}