Comenzada refactorización
- Eliminada la necesidad de JS en el índice
This commit is contained in:
parent
6fd66dbe26
commit
b0b08da6ce
389
main.go
389
main.go
|
@ -1,82 +1,26 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"igar/storage"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"mime"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/feeds"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"github.com/labstack/echo/middleware"
|
|
||||||
//"github.com/labstack/echo/middleware"
|
//"github.com/labstack/echo/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
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 {
|
|
||||||
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 {
|
TemplateRenderer struct {
|
||||||
template *template.Template
|
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 (
|
var (
|
||||||
list *entryMap = &entryMap{
|
list *storage.EntryMap = &storage.EntryMap{
|
||||||
filename: "img/list.json",
|
Filename: "img/list.json",
|
||||||
modtime: time.Unix(0, 0),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -86,224 +30,23 @@ func genName() string {
|
||||||
return strings.Replace(uuid.New().String(), "-", "", -1)
|
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
|
// TemplateRenderer
|
||||||
|
|
||||||
func (renderer *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
func (renderer *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||||
return renderer.template.ExecuteTemplate(w, name, data)
|
return renderer.template.ExecuteTemplate(w, name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// entryMap
|
func IndexController(c echo.Context) (err error) {
|
||||||
|
entries, err := list.GetEntries()
|
||||||
func (list *entryMap) Len() int { return len(list.mapped) }
|
|
||||||
|
|
||||||
func (list *entryMap) modified() (mod bool) {
|
|
||||||
info, err := os.Stat(list.filename)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
mod = list.modtime.Before(info.ModTime())
|
err = c.Render(http.StatusOK, "index.html", entries)
|
||||||
list.modtime = info.ModTime()
|
|
||||||
} else {
|
|
||||||
mod = true
|
|
||||||
}
|
}
|
||||||
return
|
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("<p>%s</p>\n<div>\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<img src=/igar/img/%s/%s>", item.Description, elem.Post, media.File)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item.Description = fmt.Sprintf("%s\n</div>", 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) {
|
func ViewController(c echo.Context) (err error) {
|
||||||
uuid := c.QueryParam("uuid")
|
uuid := c.QueryParam("uuid")
|
||||||
entry, err := list.getEntry(uuid)
|
entry, err := list.GetEntry(uuid)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = c.Render(http.StatusOK, "view.html", entry)
|
err = c.Render(http.StatusOK, "view.html", entry)
|
||||||
}
|
}
|
||||||
|
@ -313,100 +56,6 @@ func ViewController(c echo.Context) (err error) {
|
||||||
return
|
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() {
|
func main() {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|
||||||
|
@ -414,29 +63,25 @@ func main() {
|
||||||
template: template.Must(template.ParseGlob("templates/*.html")),
|
template: template.Must(template.ParseGlob("templates/*.html")),
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Static("/", "index.html")
|
e.GET("/", IndexController)
|
||||||
|
|
||||||
e.GET("/list", ListController)
|
|
||||||
e.GET("/view", ViewController)
|
e.GET("/view", ViewController)
|
||||||
|
|
||||||
e.GET("/rss", RSSController)
|
//e.GET("/list", server.ListController)
|
||||||
e.GET("/json", JSONController)
|
|
||||||
e.GET("/atom", AtomController)
|
//e.GET("/rss", server.RSSController)
|
||||||
|
//e.GET("/json", server.JSONController)
|
||||||
|
//e.GET("/atom", server.AtomController)
|
||||||
|
|
||||||
e.Static("/static", "static")
|
e.Static("/static", "static")
|
||||||
e.Static("/img", "img")
|
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 {
|
//admin.GET("/", server.AdminController)
|
||||||
return c.Render(http.StatusOK, "admin.html", struct{ List entryList }{
|
//admin.POST("/post", server.PostController)
|
||||||
List: list.getSortedEntries(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
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"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) }
|
|
@ -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("<p>%s</p>\n<div>\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<img src=/igar/img/%s/%s>", item.Description, elem.Post, media.File)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.Description = fmt.Sprintf("%s\n</div>", 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
|
||||||
|
}
|
|
@ -33,24 +33,19 @@ nav > a {
|
||||||
margin: 5px 0px;
|
margin: 5px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#load {
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
width: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.elem {
|
.elem {
|
||||||
margin: 0px auto;
|
margin: 0px auto;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
width: calc((100% - 30px) / 3);
|
width: calc((100% - 30px) / 3);
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
|
aspect-ratio: 1/1;
|
||||||
transition: transform .2s;
|
transition: transform .2s;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.elem:hover {
|
.elem:hover {
|
||||||
transform: scale(1.05);
|
transform: scale(1.025);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,48 +60,39 @@ nav > a {
|
||||||
right: 7%;
|
right: 7%;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.load {
|
@media screen and (max-width: 990px) {
|
||||||
width: 50px;
|
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 {
|
@media (prefers-color-scheme: light) {
|
||||||
margin-top: 0px;
|
nav > a {
|
||||||
}
|
background-color: #eee;
|
||||||
|
}
|
||||||
@media screen and (max-width: 990px) {
|
nav > a:hover {
|
||||||
body {
|
background-color: #ddd;
|
||||||
padding: 0px;
|
}
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<script src="static/prog.js"></script>
|
|
||||||
<script src="static/main.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1>
|
<h1>
|
||||||
<a href="/">danoloan</a>
|
<a href="..">danoloan</a>
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
bitácora fotográfica
|
bitácora fotográfica
|
||||||
|
@ -122,20 +108,18 @@ p {
|
||||||
<a class="button" href="admin/">Zona del admin 😎</a>
|
<a class="button" href="admin/">Zona del admin 😎</a>
|
||||||
</nav>
|
</nav>
|
||||||
<main>
|
<main>
|
||||||
<noscript>
|
{{ range . }}
|
||||||
<div style="font-size: 15px; text-align: left; max-width: 600px; margin:auto">
|
<a id="{{ .Post }}"
|
||||||
<p>
|
href="view?uuid={{ .Post }}"
|
||||||
Esta página utiliza código JavaScript en el navegador
|
class="elem">
|
||||||
para generar los contenidos en pantalla.
|
<img class="thumb" src="img/{{ .Post }}/thumb.jpg" loading="lazy">
|
||||||
Por favor, active JavaScript para poder usarla.
|
{{ if gt (len .Media) 1 }}
|
||||||
</p>
|
<img class="type" alt="galería" src="static/gallery.png" loading="lazy">
|
||||||
<p>
|
{{ else if eq (index .Media 0).Type "GraphVideo" }}
|
||||||
El código utilizado es bastante sencillo,
|
<img class="type" alt="galería" src="static/video.png" loading="lazy">
|
||||||
si lo quiere revisar pinche
|
{{ end }}
|
||||||
<a href="static/main.js">aquí</a>.
|
</a>
|
||||||
</p>
|
{{ end }}
|
||||||
</div>
|
|
||||||
</noscript>
|
|
||||||
</main>
|
</main>
|
||||||
<div id="load">
|
<div id="load">
|
||||||
</div>
|
</div>
|
|
@ -20,7 +20,7 @@ main > img, main > video {
|
||||||
{{if gt .Len 1 }}
|
{{if gt .Len 1 }}
|
||||||
max-height: 55vh;
|
max-height: 55vh;
|
||||||
{{ else }}
|
{{ else }}
|
||||||
max-height: 72vh;
|
max-height: 82vh;
|
||||||
{{ end }}
|
{{ end }}
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -90,30 +90,32 @@ document.getElementById("date").innerText =
|
||||||
</script>
|
</script>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
|
{{ if gt .Len 1 }}
|
||||||
<div class="count">
|
<div class="count">
|
||||||
{{ if .Count.GraphImage }}
|
{{ if .Count.GraphImage}}
|
||||||
<span class="count">
|
<span class="count">
|
||||||
{{ .Count.GraphImage }}
|
{{ .Count.GraphImage }}
|
||||||
<img src="static/image.png" alt="imágenes">
|
<img src="static/image.png" alt="imágenes">
|
||||||
</span>
|
</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if .Count.GraphVideo }}
|
{{ if .Count.GraphVideo }}
|
||||||
<span class="count">
|
<span class="count">
|
||||||
{{ .Count.GraphVideo }}
|
{{ .Count.GraphVideo }}
|
||||||
<img src="static/video.png" alt="vídeos">
|
<img src="static/video.png" alt="vídeos">
|
||||||
</span>
|
</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
<nav>
|
<nav>
|
||||||
{{ if .Next }}
|
{{ if .Next }}
|
||||||
<span class="prev">
|
<span class="prev">
|
||||||
<a href="view?uuid={{ .Next.Post }}">« sigiente</a>
|
<a href="view?uuid={{ .Next.Post }}">« sigiente</a>
|
||||||
</span>
|
</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if .Prev }}
|
{{ if .Prev }}
|
||||||
<span class="next">
|
<span class="next">
|
||||||
<a href="view?uuid={{ .Prev.Post }}">anterior »</a>
|
<a href="view?uuid={{ .Prev.Post }}">anterior »</a>
|
||||||
</span>
|
</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</nav>
|
</nav>
|
||||||
{{ $post := .Post }}
|
{{ $post := .Post }}
|
||||||
|
@ -125,6 +127,7 @@ document.getElementById("date").innerText =
|
||||||
<video src="img/{{ $post }}/{{ .File }}" controls/></video>
|
<video src="img/{{ $post }}/{{ .File }}" controls/></video>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if gt .Len 1 }}
|
||||||
<nav>
|
<nav>
|
||||||
{{ if .Next }}
|
{{ if .Next }}
|
||||||
<span class="prev">
|
<span class="prev">
|
||||||
|
@ -137,6 +140,7 @@ document.getElementById("date").innerText =
|
||||||
</span>
|
</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</nav>
|
</nav>
|
||||||
|
{{ end }}
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue