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}