Big refactor

feat/db
Antonio De Lucreziis 2 years ago
parent 0520e7641f
commit 92cee16cc8

1
.gitignore vendored

@ -17,3 +17,4 @@ public/js/
# Executables
phc-website-server
!phc-website-server/

@ -83,7 +83,7 @@ $ fd -e js | entr make frontend
- `MAIL`
Indirizzo di posta elettronica per contattare gli ammistratori del sito,
Indirizzo di posta elettronica per contattare gli amministratori del sito,
che compare nel footer di ogni pagina.
- `<SERVIZIO>_URL`

@ -0,0 +1 @@
@import"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&display=swap";:root{--fg-300: #777;--fg-400: #666;--fg-500: #333;--fg-600: #222;--bg-000: #f0f0f0;--bg-100: #f0f0f0;--bg-500: #eaeaea;--bg-550: #ecedee;--bg-600: #e4e5e7;--bg-700: #d5d5d5;--bg-750: #c8c8c8;--bg-800: #c0c0c0;--bg-850: #b8b8b8;--accent-300: #5cc969;--accent-400: #4eaa59;--accent-500: #278542;--accent-600: #2e974c;--accent-700: #154d24;--accent-800: #002d0d;--ft-ss: "Inter", sans-serif;--ft-ss-wt-light: 300;--ft-ss-wt-normal: 400;--ft-ss-wt-medium: 500;--ft-ss-wt-bold: 700;--shadow-500: 0 0 16px 0 #00000018;--text-input-bg: var(--bg-000);--text-input-readonly-bg: var(--bg-600);--text-input-readonly-fg: var(--fg-300)}*{box-sizing:border-box}html,body{margin:0}html{height:100%;width:100%}body{background:var(--bg-500);color:var(--fg-500);font-family:var(--ft-ss);font-size:17px;font-weight:var(--ft-ss-wt-normal);width:100%;min-height:100%;position:relative}h1,h2,h3,h4{margin:0;margin-top:1rem;margin-bottom:.5rem;font-weight:var(--font-weight-light)}h1{font-size:2rem}h2{font-size:1.5rem}h3{font-size:1.35rem}h4{font-size:1.2rem;font-weight:var(--font-weight-bold)}p,ul,ol,li{margin:0;width:70ch;max-width:100%;line-height:1.8}p+p{padding-top:.5rem}ul,ol{padding:0 0 0 1.5rem}hr{width:50ch;height:1px;margin:0;border:none;background-color:var(--bg-darker-2)}pre{margin:.5rem 0;background:var(--bg-lighter);border:1px solid #cbcbcb;border-radius:2px;box-shadow:0 2px 4px 0 rgba(0,0,0,.2);font-size:90%;display:flex;overflow-x:auto}pre>code{display:block;margin:.25rem}p.center{text-align:center}

Before

Width:  |  Height:  |  Size: 790 B

After

Width:  |  Height:  |  Size: 790 B

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Before

Width:  |  Height:  |  Size: 765 B

After

Width:  |  Height:  |  Size: 765 B

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before

Width:  |  Height:  |  Size: 4.6 MiB

After

Width:  |  Height:  |  Size: 4.6 MiB

@ -0,0 +1 @@
!function(a){"use strict";function o(a){return a&&"object"==typeof a&&"default"in a?a:{default:a}}var s=o(a);s.default.data("profilo",(()=>({init(){console.log("Profilo!")}}))),s.default.data("passwordForm",(()=>({password:"",passwordAgain:"",passwordSame:!0,onUpdate(){this.passwordSame=this.password===this.passwordAgain}})))}(Alpine);

@ -0,0 +1 @@
!function(e,s){"use strict";function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=t(e),r=t(s);const i={includeScore:!0,keys:["nome","cognome","tags",{name:"nomeCompleto",getFn:e=>`${e.nome} ${e.cognome}`}]},h={chronological:()=>0,name:(e,s)=>e.nome<s.nome?-1:1,surname:(e,s)=>e.cognome<s.cognome?-1:1};o.default.data("utenti",(()=>({searchField:"",sortMode:"chronological",fetchedUsers:[],sortedUserBuffer:[],fuse:new r.default([],i),searchResultsBuffer:[],searchResults:[],async init(){const e=await fetch("/api/utenti");this.fetchedUsers=await e.json(),new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&(console.log("Near the bottom of the page"),this.showMore())}))})).observe(this.$refs.spinner),this.updateSortMode(),this.updateSearch()},showMore(){const e=this.searchResults.length+15;this.searchResults=this.searchResultsBuffer.slice(0,e)},setResults(e){this.searchResultsBuffer=e.filter((e=>void 0===e.score||e.score<=.25)),this.searchResults=this.searchResultsBuffer.slice(0,15)},updateSortMode(){var e,s;this.sortedUserBuffer=(e=this.fetchedUsers,s=this.sortMode,[...e].sort(h[s])),this.fuse.setCollection(this.sortedUserBuffer),this.updateSearch()},updateSearch(){console.time("search"),0===this.searchField.trim().length?this.setResults(this.sortedUserBuffer.map((e=>({item:e})))):this.setResults(this.fuse.search(this.searchField)),console.timeEnd("search")}})))}(Alpine,Fuse);

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Before

Width:  |  Height:  |  Size: 620 KiB

After

Width:  |  Height:  |  Size: 620 KiB

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

@ -0,0 +1,41 @@
package main
import (
"log"
"git.phc.dm.unipi.it/phc/website"
"git.phc.dm.unipi.it/phc/website/articles"
"git.phc.dm.unipi.it/phc/website/auth"
"git.phc.dm.unipi.it/phc/website/config"
"git.phc.dm.unipi.it/phc/website/handler"
"git.phc.dm.unipi.it/phc/website/server"
"git.phc.dm.unipi.it/phc/website/templates"
)
func main() {
config.Load()
h := &handler.DefaultHandler{
AuthService: auth.NewDefaultService(config.AuthServiceHost),
Renderer: templates.NewRenderer(
"./views/",
"./views/base.html",
"./views/partials/*.html",
),
NewsArticlesRegistry: articles.NewRegistry("./news"),
ListaUtenti: &website.JsonFileListUtenti{
Path: "./utenti-poisson-2022.local.json",
},
Storia: &website.JsonFileStoria{
Path: "./storia.json",
},
}
app := server.NewFiberServer(h)
log.Printf("Starting server on host %q", config.Host)
err := app.Listen(config.Host)
if err != nil {
log.Fatal(err)
}
}

@ -54,8 +54,8 @@ func Load() {
loadEnv(&AuthServiceHost, "AUTH_SERVICE_HOST", "http://localhost:3535")
}
func Object() util.H {
return util.H{
func Object() util.Map {
return util.Map{
"Mode": Mode,
"Host": Host,

@ -0,0 +1,21 @@
package handler
type ContextKey[T any] string
type Context map[string]any
func GetContextValue[T any](ctx Context, key ContextKey[T]) T {
value, present := ctx[string(key)]
if !present {
var zero T
return zero
}
typedValue, _ := value.(T)
return typedValue
}
func SetContextValue[T any](ctx Context, key ContextKey[T], value T) {
ctx[string(key)] = value
}

@ -0,0 +1,147 @@
package handler
import (
"fmt"
"html/template"
"io"
"git.phc.dm.unipi.it/phc/website"
"git.phc.dm.unipi.it/phc/website/articles"
"git.phc.dm.unipi.it/phc/website/auth"
"git.phc.dm.unipi.it/phc/website/model"
"git.phc.dm.unipi.it/phc/website/templates"
"git.phc.dm.unipi.it/phc/website/util"
)
type Service interface {
HandleStaticPage(w io.Writer, view string, ctx Context) error
HandleUtenti() ([]*model.User, error)
HandleStoriaPage(w io.Writer, ctx Context) error
HandleQueryAppunti(w io.Writer, query string, ctx Context) error
HandleNewsPage(w io.Writer, ctx Context) error
HandleLogin(username, password string) (*model.Session, error)
HandleUser(token string) *model.User
HandleRequiredUser(ctx Context) (*model.User, error)
HandleProfilePage(w io.Writer, ctx Context) error
HandleArticlePage(w io.Writer, articleID string, ctx Context) error
}
//
// Typed context
//
// UserKey is a typed type for *model.User used to extract a user form a [handler.Context]
const UserKey ContextKey[*model.User] = "user"
func (ctx Context) getUser() *model.User {
return GetContextValue(ctx, UserKey)
}
// Handler holds references to abstract services for easy testing provided by every module (TODO: Make every field an interface of -Service)
type DefaultHandler struct {
AuthService auth.Service
Renderer *templates.TemplateRenderer
NewsArticlesRegistry *articles.Registry
ListaUtenti website.ListaUtentiService
Storia website.StoriaService
}
func (h *DefaultHandler) HandleStaticPage(w io.Writer, view string, ctx Context) error {
return h.Renderer.Render(w, view, util.Map{
"User": ctx.getUser(),
})
}
func (h *DefaultHandler) HandleUtenti() ([]*model.User, error) {
utenti, err := h.AuthService.GetUsers()
if err != nil {
return nil, err
}
return utenti, nil
}
func (h *DefaultHandler) HandleStoriaPage(w io.Writer, ctx Context) error {
storia, err := h.Storia.GetStoria()
if err != nil {
return err
}
return h.Renderer.Render(w, "storia.html", util.Map{
"User": ctx.getUser(),
"Storia": storia,
})
}
func (h *DefaultHandler) HandleQueryAppunti(w io.Writer, query string, ctx Context) error {
return h.Renderer.Render(w, "appunti.html", util.Map{
"User": ctx.getUser(),
"Query": query,
})
}
func (h *DefaultHandler) HandleNewsPage(w io.Writer, ctx Context) error {
articles, err := h.NewsArticlesRegistry.GetArticles()
if err != nil {
return err
}
return h.Renderer.Render(w, "news.html", util.Map{
"User": ctx.getUser(),
"Articles": articles,
})
}
func (h *DefaultHandler) HandleLogin(username, password string) (*model.Session, error) {
session, err := h.AuthService.Login(username, password)
if err != nil {
return nil, err
}
return session, nil
}
func (h *DefaultHandler) HandleUser(token string) *model.User {
user, _ := auth.UserForSession(h.AuthService, token)
return user
}
var ErrNoUser = fmt.Errorf(`user not logged in`)
func (h *DefaultHandler) HandleRequiredUser(ctx Context) (*model.User, error) {
user := ctx.getUser()
if user == nil {
return nil, ErrNoUser
}
return user, nil
}
func (h *DefaultHandler) HandleProfilePage(w io.Writer, ctx Context) error {
user := ctx.getUser()
if user == nil {
return ErrNoUser
}
return h.Renderer.Render(w, "profilo.html", util.Map{
"User": user,
})
}
func (h *DefaultHandler) HandleArticlePage(w io.Writer, articleID string, ctx Context) error {
article, err := h.NewsArticlesRegistry.GetArticle(articleID)
if err != nil {
return err
}
html, err := article.Render()
if err != nil {
return err
}
return h.Renderer.Render(w, "news-base.html", util.Map{
"User": ctx.getUser(),
"Article": article,
"ContentHTML": template.HTML(html),
})
}

@ -1,208 +0,0 @@
package main
import (
"fmt"
"html/template"
"log"
"time"
"git.phc.dm.unipi.it/phc/website/articles"
"git.phc.dm.unipi.it/phc/website/auth"
"git.phc.dm.unipi.it/phc/website/config"
"git.phc.dm.unipi.it/phc/website/model"
"git.phc.dm.unipi.it/phc/website/templates"
"git.phc.dm.unipi.it/phc/website/util"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/redirect/v2"
)
func UserMiddleware(as auth.Service) fiber.Handler {
return func(c *fiber.Ctx) error {
token := c.Cookies("session-token")
user, _ := auth.UserForSession(as, token)
c.Locals("user", user)
return c.Next()
}
}
func main() {
config.Load()
app := fiber.New()
app.Use(logger.New())
app.Use(recover.New())
// Remove trailing slash from URLs
app.Use(redirect.New(redirect.Config{
Rules: map[string]string{
"/*/": "/$1",
},
}))
// Serve content statically from "./public", mounted on the "/public/" route
app.Static("/public/", "./public")
authService := auth.NewDefaultService(config.AuthServiceHost)
app.Use(UserMiddleware(authService))
// Templates & Renderer
renderer := templates.NewRenderer(
"./views/",
"./views/base.html",
"./views/partials/*.html",
)
newsArticlesRegistry := articles.NewRegistry("./news")
// Routes
actuallyStaticRoutes := map[string]string{
"/": "home.html",
"/link": "link.html",
"/login": "login.html",
"/utenti": "utenti.html",
}
for route, view := range actuallyStaticRoutes {
localView := view
app.Get(route, func(c *fiber.Ctx) error {
c.Type("html")
return renderer.Render(c, localView, util.H{
"User": c.Locals("user"),
})
})
}
app.Get("/api/utenti", func(c *fiber.Ctx) error {
utenti, err := authService.GetUsers()
if err != nil {
return err
}
return c.JSON(utenti)
})
app.Get("/api/profilo", func(c *fiber.Ctx) error {
user := c.Locals("user")
if user == nil {
return fmt.Errorf(`user not logged in`)
}
return c.JSON(user)
})
app.Get("/storia", func(c *fiber.Ctx) error {
storia, err := GetStoria()
if err != nil {
return err
}
c.Type("html")
return renderer.Render(c, "storia.html", util.H{
"User": c.Locals("user"),
"Storia": storia,
})
})
app.Get("/appunti", func(c *fiber.Ctx) error {
searchQuery := c.Query("q", "")
c.Type("html")
return renderer.Render(c, "appunti.html", util.H{
"User": c.Locals("user"),
"Query": searchQuery,
})
})
app.Get("/news", func(c *fiber.Ctx) error {
articles, err := newsArticlesRegistry.GetArticles()
if err != nil {
return err
}
c.Type("html")
return renderer.Render(c, "news.html", util.H{
"User": c.Locals("user"),
"Articles": articles,
})
})
app.Post("/login", func(c *fiber.Ctx) error {
var loginForm struct {
Provider string `form:"provider"`
Username string `form:"username"`
Password string `form:"password"`
}
if err := c.BodyParser(&loginForm); err != nil {
return err
}
session, err := authService.Login(loginForm.Username, loginForm.Password)
if err != nil {
return err
}
inThreeDays := time.Now().Add(3 * 24 * time.Hour)
c.Cookie(&fiber.Cookie{
Name: "session-token",
Path: "/",
Value: session.Token,
Expires: inThreeDays,
})
return c.Redirect("/profilo")
})
app.Get("/profilo", func(c *fiber.Ctx) error {
user, ok := c.Locals("user").(*model.User)
if !ok || user == nil {
return fmt.Errorf(`user not logged in`)
}
c.Type("html")
return renderer.Render(c, "profilo.html", util.H{
"User": c.Locals("user"),
})
})
app.Get("/logout", func(c *fiber.Ctx) error {
c.Cookie(&fiber.Cookie{
Name: "session-token",
Path: "/",
Value: "",
Expires: time.Now(),
})
return c.Redirect("/")
})
app.Get("/news/:article", func(c *fiber.Ctx) error {
articleID := c.Params("article")
article, err := newsArticlesRegistry.GetArticle(articleID)
if err != nil {
return err
}
html, err := article.Render()
if err != nil {
return err
}
c.Type("html")
return renderer.Render(c, "news-base.html", util.H{
"User": c.Locals("user"),
"Article": article,
"ContentHTML": template.HTML(html),
})
})
log.Printf("Starting server on host %q", config.Host)
err := app.Listen(config.Host)
if err != nil {
log.Fatal(err)
}
}

@ -0,0 +1,172 @@
package server
import (
"time"
"git.phc.dm.unipi.it/phc/website/handler"
"git.phc.dm.unipi.it/phc/website/model"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/redirect/v2"
)
func UserMiddleware(h handler.Service) fiber.Handler {
return func(c *fiber.Ctx) error {
token := c.Cookies("session-token")
user := h.HandleUser(token)
c.Locals("user", user)
return c.Next()
}
}
func CreateContext(ctx *fiber.Ctx) handler.Context {
// the "_" here is required because Go cannot cast <nil> of type interface{} to *model.User, in other words <nil> of type *model.User and <nil> of type interface{} are different type. In this case the "_" returns a boolean that tells whether the cast succeeded or not, if it is false the user variable gets assigned its default zero value that is <nil> of type *model.User
user, _ := ctx.Locals("user").(*model.User)
context := handler.Context{}
handler.SetContextValue(context, handler.UserKey, user)
return context
}
func NewFiberServer(h handler.Service) *fiber.App {
app := fiber.New()
routes(h, app)
return app
}
func routes(h handler.Service, r fiber.Router) {
//
// Initial setup
//
r.Use(logger.New())
r.Use(recover.New())
// Remove trailing slash from URLs
r.Use(redirect.New(redirect.Config{
Rules: map[string]string{
"/*/": "/$1",
},
}))
// Serve content statically from "./public", mounted on the "/public/" route
r.Static("/public/", "./public")
// Process all request and add user to the request context if there is a session cookie
r.Use(UserMiddleware(h))
//
// Pages
//
r.Get("/", func(c *fiber.Ctx) error {
c.Type("html")
return h.HandleStaticPage(c, "home.html", CreateContext(c))
})
r.Get("/link", func(c *fiber.Ctx) error {
c.Type("html")
return h.HandleStaticPage(c, "link.html", CreateContext(c))
})
r.Get("/login", func(c *fiber.Ctx) error {
c.Type("html")
return h.HandleStaticPage(c, "login.html", CreateContext(c))
})
r.Get("/utenti", func(c *fiber.Ctx) error {
c.Type("html")
return h.HandleStaticPage(c, "utenti.html", CreateContext(c))
})
r.Get("/storia", func(c *fiber.Ctx) error {
c.Type("html")
return h.HandleStoriaPage(c, CreateContext(c))
})
r.Get("/appunti", func(c *fiber.Ctx) error {
query := c.Query("q", "")
c.Type("html")
return h.HandleQueryAppunti(c, query, CreateContext(c))
})
r.Get("/news", func(c *fiber.Ctx) error {
c.Type("html")
return h.HandleNewsPage(c, CreateContext(c))
})
r.Post("/login", func(c *fiber.Ctx) error {
var loginForm struct {
Provider string `form:"provider"`
Username string `form:"username"`
Password string `form:"password"`
}
if err := c.BodyParser(&loginForm); err != nil {
return err
}
session, err := h.HandleLogin(loginForm.Username, loginForm.Password)
if err != nil {
return err
}
inThreeDays := time.Now().Add(3 * 24 * time.Hour)
c.Cookie(&fiber.Cookie{
Name: "session-token",
Path: "/",
Value: session.Token,
Expires: inThreeDays,
})
return c.Redirect("/profilo")
})
r.Get("/profilo", func(c *fiber.Ctx) error {
c.Type("html")
return h.HandleProfilePage(c, CreateContext(c))
})
r.Get("/logout", func(c *fiber.Ctx) error {
c.Cookie(&fiber.Cookie{
Name: "session-token",
Path: "/",
Value: "",
Expires: time.Now(),
})
return c.Redirect("/")
})
r.Get("/news/:article", func(c *fiber.Ctx) error {
articleID := c.Params("article")
c.Type("html")
return h.HandleArticlePage(c, articleID, CreateContext(c))
})
routesApi(h, r.Group("/api"))
}
func routesApi(h handler.Service, r fiber.Router) {
r.Get("/api/utenti", func(c *fiber.Ctx) error {
utenti, err := h.HandleUtenti()
if err != nil {
return err
}
return c.JSON(utenti)
})
r.Get("/api/profilo", func(c *fiber.Ctx) error {
user, err := h.HandleRequiredUser(CreateContext(c))
if err != nil {
return err
}
return c.JSON(user)
})
}

@ -1,9 +1,9 @@
package main
package website
import (
"encoding/json"
"io/ioutil"
"math"
"os"
"sort"
"strconv"
"strings"
@ -11,23 +11,8 @@ import (
"git.phc.dm.unipi.it/phc/website/util"
)
type byEventDateDescending []*GenericEvent
func (m byEventDateDescending) Len() int {
return len(m)
}
func (m byEventDateDescending) Swap(i, j int) {
m[i], m[j] = m[j], m[i]
}
func (m byEventDateDescending) Less(i, j int) bool {
return m[i].Date > m[j].Date
}
type Macchinista struct {
Uid string `json:"uid"`
FullName string `json:"fullName"`
EntryDate string `json:"entryDate"`
ExitDate string `json:"exitDate"`
type StoriaService interface {
GetStoria() ([]*GenericEvent, error)
}
type GenericEvent struct {
@ -47,33 +32,78 @@ type GenericEvent struct {
Size int `json:"size"`
}
type HistoryDB struct {
type Macchinista struct {
Uid string `json:"uid"`
FullName string `json:"fullName"`
EntryDate string `json:"entryDate"`
ExitDate string `json:"exitDate"`
}
type storiaDB struct {
Macchinisti []*Macchinista `json:"macchinisti"`
GenericEvent []*GenericEvent `json:"eventi"`
}
func GetRawHistory() (*HistoryDB, error) {
var rawHistory HistoryDB
type JsonFileStoria struct {
Path string
}
type byEventDateDescending []*GenericEvent
historyDB, err := ioutil.ReadFile("./storia.json")
func (m byEventDateDescending) Len() int {
return len(m)
}
func (m byEventDateDescending) Swap(i, j int) {
m[i], m[j] = m[j], m[i]
}
func (m byEventDateDescending) Less(i, j int) bool {
return m[i].Date > m[j].Date
}
func (db *JsonFileStoria) GetStoria() ([]*GenericEvent, error) {
history, err := db.GetRawStoria()
if err != nil {
return nil, err
}
if err := json.Unmarshal(historyDB, &rawHistory); err != nil {
return nil, err
events := history.GenericEvent[:]
for _, m := range history.Macchinisti {
if m.ExitDate != "" {
events = append(events, &GenericEvent{
Type: "exit-macchinista",
Uid: m.Uid,
FullName: m.FullName,
Date: m.ExitDate,
})
}
if m.EntryDate != "" {
events = append(events, &GenericEvent{
Type: "entry-macchinista",
Uid: m.Uid,
FullName: m.FullName,
Date: m.EntryDate,
})
}
}
return &rawHistory, nil
sort.Sort(byEventDateDescending(events))
return withSpacers(events), nil
}
func getDateYear(date string) int {
year, err := strconv.Atoi(strings.Split(date, "/")[0])
func (db *JsonFileStoria) GetRawStoria() (*storiaDB, error) {
var rawHistory storiaDB
historyDB, err := os.ReadFile(db.Path)
if err != nil {
panic(err) // Tanto nel caso si nota in fase di sviluppo visto che "storia.json" è statico
return nil, err
}
return year
if err := json.Unmarshal(historyDB, &rawHistory); err != nil {
return nil, err
}
return &rawHistory, nil
}
func withSpacers(events []*GenericEvent) []*GenericEvent {
@ -97,33 +127,11 @@ func withSpacers(events []*GenericEvent) []*GenericEvent {
return newEvents
}
func GetStoria() ([]*GenericEvent, error) {
history, err := GetRawHistory()
func getDateYear(date string) int {
year, err := strconv.Atoi(strings.Split(date, "/")[0])
if err != nil {
return nil, err
}
events := history.GenericEvent[:]
for _, m := range history.Macchinisti {
if m.ExitDate != "" {
events = append(events, &GenericEvent{
Type: "exit-macchinista",
Uid: m.Uid,
FullName: m.FullName,
Date: m.ExitDate,
})
}
if m.EntryDate != "" {
events = append(events, &GenericEvent{
Type: "entry-macchinista",
Uid: m.Uid,
FullName: m.FullName,
Date: m.EntryDate,
})
}
panic(err) // Tanto nel caso si nota in fase di sviluppo visto che "storia.json" è statico
}
sort.Sort(byEventDateDescending(events))
return withSpacers(events), nil
return year
}

@ -91,16 +91,16 @@ func (r *TemplateRenderer) Load(name string) *CachedTemplate {
}
// Render the template, also injects "Page" and "Config" values in the template
func (r *TemplateRenderer) Render(w io.Writer, name string, data util.H) error {
func (r *TemplateRenderer) Render(w io.Writer, name string, data util.Map) error {
cachedTemplate := r.Load(name)
if config.Mode == "development" {
cachedTemplate.Reload()
}
newData := util.H{}
newData := util.Map{}
newData.Apply(data)
newData["Page"] = util.H{
newData["Page"] = util.Map{
// Used to inject a page specific class on <body>
"Name": strings.TrimSuffix(path.Base(name), ".html"),
}

@ -1,8 +1,8 @@
package main
package website
import (
"encoding/json"
"io/ioutil"
"os"
)
type UserInfo struct {
@ -14,21 +14,18 @@ type UserInfo struct {
IsMacchinista bool `json:"macchinista,omitempty"`
}
func GetUtenti() ([]UserInfo, error) {
history, err := GetRawHistory()
if err != nil {
return nil, err
}
type ListaUtentiService interface {
GetUtenti() ([]UserInfo, error)
}
// Creo un set per velocizzare l'utilizzo dopo
macchinisti := map[string]bool{}
for _, m := range history.Macchinisti {
macchinisti[m.Uid] = true
}
type JsonFileListUtenti struct {
Path string
}
func (j *JsonFileListUtenti) GetUtenti() ([]UserInfo, error) {
var users []UserInfo
usersJsonData, err := ioutil.ReadFile("./utenti-poisson-2022.local.json")
usersJsonData, err := os.ReadFile(j.Path)
if err != nil {
return nil, err
}

@ -1 +0,0 @@
package util

@ -1,10 +1,10 @@
package util
// H is a shortcut for creating a json-like object
type H map[string]interface{}
// Map is a shortcut for creating a json-like object
type Map map[string]interface{}
// Apply is like Object.apply from JS and merges the given objects on top of target
func (target H) Apply(sources ...H) H {
func (target Map) Apply(sources ...Map) Map {
for _, source := range sources {
for k, v := range source {
target[k] = v

@ -6,6 +6,7 @@ import (
"log"
)
// GenerateRandomString generates a random base64 string from a random array of "n" bytes (to prevent padding let n be a multiple of 3)
func GenerateRandomString(n int) string {
b := make([]byte, n)

Loading…
Cancel
Save