1package main
  2
  3import (
  4	"encoding/csv"
  5	"fmt"
  6	"log"
  7	"net/http"
  8	"os"
  9	"sort"
 10	"strings"
 11	"time"
 12
 13	"github.com/microcosm-cc/bluemonday"
 14)
 15
 16type (
 17	Model struct {
 18		Projects []project
 19	}
 20
 21	project struct {
 22		URL      string
 23		Name     string
 24		Forge    string
 25		Running  string
 26		Releases []release
 27	}
 28
 29	release struct {
 30		Tag     string
 31		Content string
 32		URL     string
 33		Date    time.Time
 34	}
 35)
 36
 37var (
 38	req           = make(chan struct{})
 39	manualRefresh = make(chan struct{})
 40	res           = make(chan []project)
 41	m             = Model{
 42		Projects: []project{},
 43	}
 44	bmUGC    = bluemonday.UGCPolicy()
 45	bmStrict = bluemonday.StrictPolicy()
 46)
 47
 48func main() {
 49	file, err := os.Open("projects.csv")
 50	if err != nil {
 51		log.Fatalln(err)
 52	}
 53	defer file.Close()
 54
 55	reader := csv.NewReader(file)
 56
 57	records, err := reader.ReadAll()
 58	if err != nil {
 59		log.Fatalln(err)
 60	}
 61
 62	m.Projects = []project{}
 63	if len(records) > 0 {
 64		for i, record := range records {
 65			if i == 0 {
 66				continue
 67			}
 68			m.Projects = append(m.Projects, project{
 69				URL:      record[0],
 70				Name:     record[1],
 71				Forge:    record[2],
 72				Running:  record[3],
 73				Releases: []release{},
 74			})
 75		}
 76	}
 77
 78	go refreshLoop(manualRefresh, req, res)
 79
 80	mux := http.NewServeMux()
 81
 82	httpServer := &http.Server{
 83		Addr:    "0.0.0.0:1337",
 84		Handler: mux,
 85	}
 86
 87	mux.HandleFunc("/", rootHandler)
 88	mux.HandleFunc("/static", staticHandler)
 89	mux.HandleFunc("/new", newHandler)
 90
 91	if err := httpServer.ListenAndServe(); err == http.ErrServerClosed {
 92		log.Println("Web server closed")
 93	} else {
 94		log.Fatalln(err)
 95	}
 96}
 97
 98func refreshLoop(manualRefresh, req chan struct{}, res chan []project) {
 99	ticker := time.NewTicker(time.Second * 3600)
100
101	yoink := func() []project {
102		projects := make([]project, len(m.Projects))
103		copy(projects, m.Projects)
104		for i, project := range projects {
105			project, err := getReleases(project)
106			if err != nil {
107				fmt.Println(err)
108				continue
109			}
110			projects[i] = project
111		}
112		sort.Slice(projects, func(i, j int) bool { return strings.ToLower(projects[i].Name) < strings.ToLower(projects[j].Name) })
113		return projects
114	}
115
116	projects := yoink()
117
118	for {
119		select {
120		case <-ticker.C:
121			projects = yoink()
122		case <-manualRefresh:
123			ticker.Reset(time.Second * 3600)
124			projects = yoink()
125		case <-req:
126			projectsCopy := make([]project, len(projects))
127			copy(projectsCopy, projects)
128			res <- projectsCopy
129		}
130	}
131}