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