Refactor de subidas y eliminaciones
This commit is contained in:
parent
b110cdcacc
commit
b37dd824de
62
main.go
62
main.go
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
"html/template"
|
"html/template"
|
||||||
"igar/storage"
|
"igar/storage"
|
||||||
"io"
|
"io"
|
||||||
|
@ -9,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/labstack/echo/middleware"
|
||||||
//"github.com/labstack/echo/middleware"
|
//"github.com/labstack/echo/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,6 +59,53 @@ func ViewController(c echo.Context) (err error) {
|
||||||
return
|
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() {
|
func main() {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|
||||||
|
@ -66,8 +116,6 @@ func main() {
|
||||||
e.GET("/", IndexController)
|
e.GET("/", IndexController)
|
||||||
e.GET("/view", ViewController)
|
e.GET("/view", ViewController)
|
||||||
|
|
||||||
//e.GET("/list", server.ListController)
|
|
||||||
|
|
||||||
//e.GET("/rss", server.RSSController)
|
//e.GET("/rss", server.RSSController)
|
||||||
//e.GET("/json", server.JSONController)
|
//e.GET("/json", server.JSONController)
|
||||||
//e.GET("/atom", server.AtomController)
|
//e.GET("/atom", server.AtomController)
|
||||||
|
@ -75,13 +123,13 @@ func main() {
|
||||||
e.Static("/static", "static")
|
e.Static("/static", "static")
|
||||||
e.Static("/img", "/var/lib/igar")
|
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.GET("/", AdminController)
|
||||||
//admin.POST("/post", server.PostController)
|
admin.POST("/post", PostController)
|
||||||
//admin.DELETE("/post", DeleteController)
|
admin.DELETE("/post", DeleteController)
|
||||||
|
|
||||||
//admin.Static("/static", "admin/static")
|
admin.Static("/static", "admin/static")
|
||||||
|
|
||||||
e.Logger.Fatal(e.Start(":40404"))
|
e.Logger.Fatal(e.Start(":40404"))
|
||||||
}
|
}
|
||||||
|
|
121
storage/entry.go
121
storage/entry.go
|
@ -2,20 +2,18 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime"
|
||||||
|
"mime/multipart"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
JSTime time.Time
|
|
||||||
|
|
||||||
Media struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
File string `json:"file"`
|
|
||||||
Height int `json:"height"`
|
|
||||||
Width int `json:"width"`
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry struct {
|
Entry struct {
|
||||||
Date JSTime `json:"date"`
|
Date JSTime `json:"date"`
|
||||||
Description string `json:"description"`
|
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 {
|
func (entry Entry) Count() map[string]int {
|
||||||
count := make(map[string]int)
|
count := make(map[string]int)
|
||||||
for _, media := range entry.Media {
|
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) 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
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -12,7 +10,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/feeds"
|
"github.com/gorilla/feeds"
|
||||||
"github.com/labstack/echo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -26,7 +23,13 @@ type (
|
||||||
entryList []*Entry
|
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) {
|
func (list *EntryMap) modified() (mod bool) {
|
||||||
info, err := os.Stat(list.Filename)
|
info, err := os.Stat(list.Filename)
|
||||||
|
@ -76,45 +79,34 @@ func (list *EntryMap) read() (err error) {
|
||||||
return
|
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()
|
list.mu.RLock()
|
||||||
defer list.mu.RUnlock()
|
defer list.mu.RUnlock()
|
||||||
|
|
||||||
err = list.read()
|
err = list.read()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
result = list.sorted
|
result = make([]Entry, len(list.sorted))
|
||||||
|
for index, elem := range list.sorted {
|
||||||
|
result[index] = *elem
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (list *EntryMap) addEntry(entry *Entry) (err error) {
|
func (list *EntryMap) GetFeed() (feed *feeds.Feed) {
|
||||||
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{
|
feed = &feeds.Feed{
|
||||||
Title: "danoloan.es igar",
|
Title: "danoloan.es igar",
|
||||||
Link: &feeds.Link{Href: "https://danoloan.es/igar/"},
|
Link: &feeds.Link{Href: "https://danoloan.es/igar/"},
|
||||||
|
@ -149,19 +141,34 @@ func (list *EntryMap) getFeed() (feed *feeds.Feed) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// entryList
|
func (list *EntryMap) Len() int { return len(list.mapped) }
|
||||||
|
|
||||||
func (list entryList) Len() int { return len(list) }
|
func (list *EntryMap) AddEntry(entry *Entry) (err error) {
|
||||||
func (list entryList) Swap(i, j int) { list[i], list[j] = list[j], list[i] }
|
list.mu.Lock()
|
||||||
func (list entryList) Less(i, j int) bool {
|
defer list.mu.Unlock()
|
||||||
return time.Time(list[i].Date).After(time.Time(list[j].Date))
|
|
||||||
}
|
|
||||||
|
|
||||||
func auth(username, password string, c echo.Context) (bool, error) {
|
list.read()
|
||||||
hash := sha512.Sum512([]byte(password))
|
|
||||||
hashString := hex.EncodeToString(hash[:])
|
if entry != nil {
|
||||||
if username == adminUsername && hashString == adminPassword {
|
list.mapped[entry.Post] = entry
|
||||||
return true, nil
|
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
|
||||||
}
|
}
|
|
@ -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())
|
||||||
|
}
|
|
@ -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"`
|
||||||
|
}
|
||||||
|
)
|
|
@ -24,7 +24,7 @@
|
||||||
<button class="cajetin">Archivar/Eliminar imagen</button>
|
<button class="cajetin">Archivar/Eliminar imagen</button>
|
||||||
<div class="action list">
|
<div class="action list">
|
||||||
<div class="scroll" id="dellist">
|
<div class="scroll" id="dellist">
|
||||||
{{ range .List }}
|
{{ range . }}
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<div class="date"> {{ .Date }} </div>
|
<div class="date"> {{ .Date }} </div>
|
||||||
<div class="desc"> {{ .Description }} </div>
|
<div class="desc"> {{ .Description }} </div>
|
||||||
|
@ -68,7 +68,7 @@ function open(uuid) {
|
||||||
|
|
||||||
async function archive(uuid) {
|
async function archive(uuid) {
|
||||||
if (confirm('Vas a archivar una imagen. ¿Estás seguro?')) {
|
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) {
|
if (response.ok) {
|
||||||
remove(this);
|
remove(this);
|
||||||
result(this, 'Imagen archivada');
|
result(this, 'Imagen archivada');
|
||||||
|
@ -81,7 +81,7 @@ async function archive(uuid) {
|
||||||
|
|
||||||
async function delpost(uuid) {
|
async function delpost(uuid) {
|
||||||
if (confirm('Vas a eliminar una imagen. ¿Estás seguro?')) {
|
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) {
|
if (response.ok) {
|
||||||
remove(this);
|
remove(this);
|
||||||
result(this, 'Imagen eliminada');
|
result(this, 'Imagen eliminada');
|
||||||
|
@ -141,7 +141,7 @@ form.onsubmit = function(event) {
|
||||||
xhr.open('POST', 'post', true);
|
xhr.open('POST', 'post', true);
|
||||||
|
|
||||||
xhr.upload.onprogress = (prog) => {
|
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
|
// Set up a handler for when the task for the request is complete
|
||||||
|
|
|
@ -75,7 +75,7 @@ span.count {
|
||||||
{{ .Description }}
|
{{ .Description }}
|
||||||
</div>
|
</div>
|
||||||
<div id="date">
|
<div id="date">
|
||||||
{{ .Date.String }}
|
{{ .Date }}
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
;
|
;
|
||||||
|
|
Loading…
Reference in New Issue