From b0b08da6ce60611b9fd07254f1a07e8441ee3131 Mon Sep 17 00:00:00 2001 From: danoloan Date: Sat, 12 Feb 2022 10:29:56 +0100 Subject: [PATCH] =?UTF-8?q?Comenzada=20refactorizaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Eliminada la necesidad de JS en el índice --- main.go | 389 ++--------------------------- storage/entry.go | 71 ++++++ storage/list.go | 167 +++++++++++++ index.html => templates/index.html | 96 +++---- templates/view.html | 36 +-- 5 files changed, 315 insertions(+), 444 deletions(-) create mode 100644 storage/entry.go create mode 100644 storage/list.go rename index.html => templates/index.html (56%) diff --git a/main.go b/main.go index d2d550a..531713c 100644 --- a/main.go +++ b/main.go @@ -1,82 +1,26 @@ package main import ( - "crypto/sha512" - "encoding/hex" - "encoding/json" - "fmt" "html/template" + "igar/storage" "io" - "io/ioutil" - "mime" "net/http" - "os" - "os/exec" - "sort" - "strconv" "strings" - "sync" - "time" "github.com/google/uuid" - "github.com/gorilla/feeds" "github.com/labstack/echo" - "github.com/labstack/echo/middleware" //"github.com/labstack/echo/middleware" ) 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"` - Post string `json:"post"` - Likes int `json:"likes"` - Tags []string `json:"tags"` - Media []Media `json:"media"` - Prev *Entry `json:"-"` - Next *Entry `json:"-"` - // Users []string - } - Response struct { - Ok bool - Message string - } TemplateRenderer struct { template *template.Template } ) -type ( - entryMap struct { - filename string - modtime time.Time - mapped map[string]*Entry - sorted []*Entry - mu sync.Mutex - } - entryList []*Entry -) - -const ( - jsTimeLite = "2006-01-02" - jsTimeLayout = "2006-01-02 15:04:05" - adminUsername = "danolo" - adminPassword = "bd4cad796950f50352225de3c773d8f3c39622bc17f34ad661eabe615cdf6d32751c5751e0648dc17d890f40330018334a2ae899878f200f6dc80121ddb70cc9" -) - var ( - list *entryMap = &entryMap{ - filename: "img/list.json", - modtime: time.Unix(0, 0), + list *storage.EntryMap = &storage.EntryMap{ + Filename: "img/list.json", } ) @@ -86,224 +30,23 @@ func genName() string { return strings.Replace(uuid.New().String(), "-", "", -1) } -// 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 { - count[media.Type] += 1 - } - return count -} - -func (entry Entry) Len() int { return len(entry.Media) } - // TemplateRenderer func (renderer *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error { return renderer.template.ExecuteTemplate(w, name, data) } -// entryMap - -func (list *entryMap) Len() int { return len(list.mapped) } - -func (list *entryMap) modified() (mod bool) { - info, err := os.Stat(list.filename) +func IndexController(c echo.Context) (err error) { + entries, err := list.GetEntries() if err == nil { - mod = list.modtime.Before(info.ModTime()) - list.modtime = info.ModTime() - } else { - mod = true + err = c.Render(http.StatusOK, "index.html", entries) } return } -func (list *entryMap) write() (err error) { - data, err := json.MarshalIndent(list.mapped, "", " ") - if err == nil { - err = ioutil.WriteFile(list.filename, data, 0644) - } - return -} - -func (list *entryMap) read() (err error) { - if list.modified() { - if data, err := ioutil.ReadFile(list.filename); err == nil { - - // read map - list.mapped = make(map[string]*Entry) - err = json.Unmarshal(data, &list.mapped) - - // sort list - list.sorted = make([]*Entry, 0, len(list.mapped)) - for _, entry := range list.mapped { - list.sorted = append(list.sorted, entry) - } - sort.Sort(entryList(list.sorted)) - - // assign prev/next - for index, value := range list.sorted { - if index-1 >= 0 { - value.Next = list.sorted[index-1] - } - if index+1 < len(list.sorted) { - value.Prev = list.sorted[index+1] - } - } - } - } - return -} - -func (list *entryMap) getSortedEntries() entryList { - list.mu.Lock() - defer list.mu.Unlock() - - list.read() - - return entryList(list.sorted) -} - -func (list *entryMap) readSliceList(oval int, nval int) (result entryList, err error) { - if nval <= 0 { - nval = list.Len() - } - - if oval <= 0 { - oval = 0 - } - - if err == nil { - entries := list.getSortedEntries() - if oval >= list.Len() { - result = nil - } else if oval+nval >= list.Len() { - result = entries[oval:] - } else { - result = entries[oval : oval+nval] - } - } - - 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) { - feed = &feeds.Feed{ - Title: "danoloan.es igar", - Link: &feeds.Link{Href: "https://danoloan.es/igar/"}, - Description: "bitácora fotográfica de danoloan", - Author: &feeds.Author{Name: "danoloan", Email: "danolo@danoloan.es"}, - } - - feed.Items = make([]*feeds.Item, 0, list.Len()) - for _, elem := range list.getSortedEntries() { - item := &feeds.Item{ - Title: elem.Description, - Link: &feeds.Link{Href: fmt.Sprintf("https://danoloan.es/igar/view?uuid=%s", elem.Post)}, - Description: fmt.Sprintf("

%s

\n
\n", elem.Description), - Created: time.Time(elem.Date), - } - - for _, media := range elem.Media { - if media.Type == "GraphImage" { - // TODO ruta relativa al proxy - item.Description = fmt.Sprintf("%s\n\t", item.Description, elem.Post, media.File) - } - } - item.Description = fmt.Sprintf("%s\n
", item.Description) - - feed.Items = append(feed.Items, item) - } - - return -} - -// 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)) -} - -// CONTROLLERS - -func ListController(c echo.Context) (res error) { - var err error - var oval, nval int - - oval, err = strconv.Atoi(c.QueryParam("o")) - if err != nil { - oval = 0 - } - - nval, err = strconv.Atoi(c.QueryParam("n")) - if err != nil { - nval = 0 - } - - slice, err := list.readSliceList(oval, nval) - if err == nil { - res = c.JSON(http.StatusOK, slice) - } else { - response := Response{false, "Error fetching song list"} - res = c.JSON(http.StatusInternalServerError, response) - } - - return -} - func ViewController(c echo.Context) (err error) { uuid := c.QueryParam("uuid") - entry, err := list.getEntry(uuid) + entry, err := list.GetEntry(uuid) if err == nil { err = c.Render(http.StatusOK, "view.html", entry) } @@ -313,100 +56,6 @@ func ViewController(c echo.Context) (err error) { return } -func RSSController(c echo.Context) (err error) { - if blob, err := list.getFeed().ToRss(); err == nil { - c.Blob(http.StatusOK, "application/xml", []byte(blob)) - } - return -} - -func JSONController(c echo.Context) (err error) { - if blob, err := list.getFeed().ToJSON(); err == nil { - c.Blob(http.StatusOK, "application/json", []byte(blob)) - } - return -} - -func AtomController(c echo.Context) (err error) { - if blob, err := list.getFeed().ToAtom(); err == nil { - c.Blob(http.StatusOK, "application/xml", []byte(blob)) - } - return -} - -// TODO pura mierda -func PostController(c echo.Context) (err error) { - desc := c.FormValue("desc") - date := c.FormValue("date") - - file, err := c.FormFile("file") - - entry := &Entry{ - Description: desc, - Post: genName(), - } - - if date != "" { - time, _ := time.Parse(jsTimeLite, date) - entry.Date = JSTime(time) - } else { - entry.Date = JSTime(time.Now()) - } - - if err != nil { - return - } - - media := Media{ - Type: "GraphImage", - File: genName(), - } - - extension, err := mime.ExtensionsByType(file.Header.Get("Content-Type")) - if err == nil { - media.File = fmt.Sprintf("%s%s", media.File, extension[0]) - } - - entry.Media = append(entry.Media, media) - - src, err := file.Open() - if err != nil { - return - } - defer src.Close() - - postdir := fmt.Sprintf("img/%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() - - list.addEntry(entry) - - if _, err = io.Copy(dst, src); err != nil { - return - } - - if err = exec.Command("node", "thumbs/make_thumb.js", postdir).Run(); err != nil { - c.Logger().Error(err.Error()) - } - - return c.String(http.StatusCreated, "") -} - -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 - } - return false, nil -} - func main() { e := echo.New() @@ -414,29 +63,25 @@ func main() { template: template.Must(template.ParseGlob("templates/*.html")), } - e.Static("/", "index.html") - - e.GET("/list", ListController) + e.GET("/", IndexController) e.GET("/view", ViewController) - e.GET("/rss", RSSController) - e.GET("/json", JSONController) - e.GET("/atom", AtomController) + //e.GET("/list", server.ListController) + + //e.GET("/rss", server.RSSController) + //e.GET("/json", server.JSONController) + //e.GET("/atom", server.AtomController) e.Static("/static", "static") e.Static("/img", "img") - admin := e.Group("/admin", middleware.BasicAuth(auth)) + //admin := e.Group("/admin", middleware.BasicAuth(auth)) - admin.GET("/", func(c echo.Context) error { - return c.Render(http.StatusOK, "admin.html", struct{ List entryList }{ - List: list.getSortedEntries(), - }) - }) - admin.POST("/post", PostController) + //admin.GET("/", server.AdminController) + //admin.POST("/post", server.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 new file mode 100644 index 0000000..67371c7 --- /dev/null +++ b/storage/entry.go @@ -0,0 +1,71 @@ +package storage + +import ( + "fmt" + "strings" + "time" +) + +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"` + Post string `json:"post"` + Likes int `json:"likes"` + Tags []string `json:"tags"` + Media []Media `json:"media"` + Prev *Entry `json:"-"` + Next *Entry `json:"-"` + // Users []string + } +) + +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 { + count[media.Type] += 1 + } + return count +} + +func (entry Entry) Len() int { return len(entry.Media) } diff --git a/storage/list.go b/storage/list.go new file mode 100644 index 0000000..d4919bf --- /dev/null +++ b/storage/list.go @@ -0,0 +1,167 @@ +package storage + +import ( + "crypto/sha512" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "sort" + "sync" + "time" + + "github.com/gorilla/feeds" + "github.com/labstack/echo" +) + +type ( + EntryMap struct { + Filename string + modtime time.Time + mapped map[string]*Entry + sorted []*Entry + mu sync.RWMutex + } + entryList []*Entry +) + +func (list *EntryMap) Len() int { return len(list.mapped) } + +func (list *EntryMap) modified() (mod bool) { + info, err := os.Stat(list.Filename) + if err == nil { + mod = list.modtime.Before(info.ModTime()) + list.modtime = info.ModTime() + } else { + mod = true + } + return +} + +func (list *EntryMap) write() (err error) { + data, err := json.MarshalIndent(list.mapped, "", " ") + if err == nil { + err = ioutil.WriteFile(list.Filename, data, 0644) + } + return +} + +func (list *EntryMap) read() (err error) { + if list.modified() { + if data, err := ioutil.ReadFile(list.Filename); err == nil { + + // read map + list.mapped = make(map[string]*Entry) + err = json.Unmarshal(data, &list.mapped) + + // sort list + list.sorted = make([]*Entry, 0, len(list.mapped)) + for _, entry := range list.mapped { + list.sorted = append(list.sorted, entry) + } + sort.Sort(entryList(list.sorted)) + + // assign prev/next + for index, value := range list.sorted { + if index-1 >= 0 { + value.Next = list.sorted[index-1] + } + if index+1 < len(list.sorted) { + value.Prev = list.sorted[index+1] + } + } + } + } + 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 + } + + 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) { + feed = &feeds.Feed{ + Title: "danoloan.es igar", + Link: &feeds.Link{Href: "https://danoloan.es/igar/"}, + Description: "bitácora fotográfica de danoloan", + Author: &feeds.Author{Name: "danoloan", Email: "danolo@danoloan.es"}, + } + + feed.Items = make([]*feeds.Item, 0, list.Len()) + + elements, err := list.GetEntries() + if err != nil { + for _, elem := range elements { + item := &feeds.Item{ + Title: elem.Description, + Link: &feeds.Link{Href: fmt.Sprintf("https://danoloan.es/igar/view?uuid=%s", elem.Post)}, + Description: fmt.Sprintf("

%s

\n
\n", elem.Description), + Created: time.Time(elem.Date), + } + + for _, media := range elem.Media { + if media.Type == "GraphImage" { + // TODO ruta relativa al proxy + item.Description = fmt.Sprintf("%s\n\t", item.Description, elem.Post, media.File) + } + } + item.Description = fmt.Sprintf("%s\n
", item.Description) + + feed.Items = append(feed.Items, item) + } + + } + return +} + +// 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 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 + } + return false, nil +} diff --git a/index.html b/templates/index.html similarity index 56% rename from index.html rename to templates/index.html index 98f19b7..716040c 100644 --- a/index.html +++ b/templates/index.html @@ -33,24 +33,19 @@ nav > a { margin: 5px 0px; } -#load { - text-align: center; - padding: 20px; - width: 10em; -} - .elem { margin: 0px auto; padding: 5px; width: calc((100% - 30px) / 3); max-width: 320px; + aspect-ratio: 1/1; transition: transform .2s; position: relative; display: inline-block; } .elem:hover { - transform: scale(1.05); + transform: scale(1.025); z-index: 10; } @@ -65,48 +60,39 @@ nav > a { right: 7%; } -img.load { - width: 50px; -} + @media screen and (max-width: 990px) { + body { + padding: 0px; + } + header { + padding: 0px 1em; + } + nav { + position: initial; + text-align: center; + margin-bottom: 1em; + } + .elem { + padding: 1px; + width: calc((100% - 6px) / 3); + } + } -p { - margin-top: 0px; -} - - @media screen and (max-width: 990px) { - body { - padding: 0px; + @media (prefers-color-scheme: light) { + nav > a { + background-color: #eee; + } + nav > a:hover { + background-color: #ddd; + } } - header { - padding: 0px 1em; - } - nav { - position: initial; - text-align: center; - margin-bottom: 1em; - } - .elem { - padding: 1px; - width: calc((100% - 6px) / 3); - } - } - - @media (prefers-color-scheme: light) { - nav > a { - background-color: #eee; - } - nav > a:hover { - background-color: #ddd; - } - } - - +

- danoloan + danoloan

bitácora fotográfica @@ -122,20 +108,18 @@ p { Zona del admin 😎

- + {{ range . }} + + + {{ if gt (len .Media) 1 }} + galería + {{ else if eq (index .Media 0).Type "GraphVideo" }} + galería + {{ end }} + + {{ end }}
diff --git a/templates/view.html b/templates/view.html index ea04131..f558cc1 100644 --- a/templates/view.html +++ b/templates/view.html @@ -20,7 +20,7 @@ main > img, main > video { {{if gt .Len 1 }} max-height: 55vh; {{ else }} - max-height: 72vh; + max-height: 82vh; {{ end }} max-width: 100%; width: auto; @@ -90,30 +90,32 @@ document.getElementById("date").innerText =
+ {{ if gt .Len 1 }}
- {{ if .Count.GraphImage }} - - {{ .Count.GraphImage }} - imágenes - + {{ if .Count.GraphImage}} + + {{ .Count.GraphImage }} + imágenes + {{ end }} {{ if .Count.GraphVideo }} - - {{ .Count.GraphVideo }} - vídeos - + + {{ .Count.GraphVideo }} + vídeos + {{ end }}
+ {{ end }} {{ $post := .Post }} @@ -125,6 +127,7 @@ document.getElementById("date").innerText = {{ end }} {{ end }} + {{ if gt .Len 1 }} + {{ end }}