-
Notifications
You must be signed in to change notification settings - Fork 19
/
main.go
272 lines (240 loc) · 6.31 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/gorilla/pat"
_ "github.com/lib/pq"
"github.com/markbates/goth/gothic"
"github.com/pkg/errors"
"github.com/unrolled/render"
)
type Organization struct {
Title string
ImagePath string
Visible bool
}
// Any project under one of these organizations counts
var orgs = map[string]Organization{
"devict": {
Title: "devICT",
ImagePath: "public/images/logos/devict.svg",
Visible: true,
},
"grooverlabs": {
Title: "Groover Labs",
ImagePath: "public/images/logos/grooverlabs.png",
Visible: true,
},
"makeict": {
Title: "makeICT",
ImagePath: "public/images/logos/makeict.svg",
Visible: true,
},
"lake-afton-public-observatory": {
Title: "Lake Afton Public Observatory",
ImagePath: "public/images/logos/lake-afton.png",
Visible: true,
},
}
type Sponsor struct {
Name string
URL string
ImagePath string
ImageClasses string
}
var sponsors = []Sponsor{
{
Name: "Moonbase Labs",
URL: "https://moonbaselabs.com",
ImagePath: "public/images/logos/moonbaselabs.svg",
ImageClasses: "",
},
{
Name: "Quilibrium",
URL: "https://quilibrium.com/",
ImagePath: "public/images/logos/quilibrium.svg",
ImageClasses: "",
},
}
type Project struct {
Title string
Description string
Visible bool
}
type HacktoberfestConfiguration struct {
RequiredPullRequestCount int
RequiredPullRequestCountEng string
TimesToRepeat string
DisplaySponsors bool
}
var hacktoberfestOptions = HacktoberfestConfiguration{
RequiredPullRequestCount: 2, // the number of pull requests required to qualify for this year.
RequiredPullRequestCountEng: "two pull requests", // the required number written out fully
TimesToRepeat: "Repeat 1 more times.", // the value in this sentence should be one less than the required count.
DisplaySponsors: false,
}
// These specific projects also count
var projects = map[string]Project{
"imacrayon/eventsinwichita": {
Title: "Events in Wichita",
Description: "",
Visible: false,
},
"br0xen/boltbrowser": {
Title: "Bolt Browser",
Description: "A CLI Browser for BoltDB Files.",
Visible: false,
},
"benblankley/fort-rpg": {
Title: "fort-rpg",
Description: "A text-based Computer Role Playing Game written in Fortran 90.",
Visible: true,
},
"blunket/camelot": {
Title: "Camelot",
Description: "The 2-player strategy board game, Camelot! (a.k.a. Inside Moves)",
Visible: true,
},
"kentonh/ProjectNameGenerator": {
Title: "Project Name Generator",
Description: "Really stupid way to give your project a code name.",
Visible: true,
},
"sethetter/reqq": {
Title: "reqq",
Description: "A rust CLI for making predefined HTTP requests.",
Visible: true,
},
"sethetter/linktrap": {
Title: "linktrap",
Description: "Text a link to a twilio-number, get back an archived and unpaywalled version.",
Visible: true,
},
"imacrayon/whatthetofu": {
Title: "What the Tofu",
Description: "Find vegan food in Wichita.",
Visible: true,
},
"imacrayon/alpine-ajax": {
Title: "Alpine AJAX",
Description: "An Alpine.js plugin for building AJAX-powered frontends.",
Visible: true,
},
"imacrayon/snowbodyknows": {
Title: "Snowbody Knows",
Description: "A secret santa wishlist builder.",
Visible: true,
},
}
var v = render.New(render.Options{
Layout: "layout",
IsDevelopment: dev(),
})
var db *sql.DB
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
if err := setupDB(); err != nil {
log.Println("could not set up db", err)
os.Exit(1)
}
run := "web"
if len(os.Args) > 1 {
run = os.Args[1]
}
if run == "check" {
if err := check(); err != nil {
log.Fatal(err)
}
return
}
// default: web
r := pat.New()
// Register auth handlers. pat requires all routes be registered most
// specific first so the shorter routes have to be added last
r.Get("/auth/{provider}/callback", authCallback)
r.Get("/auth/{provider}", gothic.BeginAuthHandler)
r.Get("/api/issues", issues)
r.Get("/api/prs", prs)
r.Get("/api/share", getShare)
r.Put("/api/share", updateShare)
r.Get("/profile", profile)
// Serve static files
r.PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir("public"))))
r.Get("/", home)
addr := ":8080"
if p := os.Getenv("PORT"); p != "" {
addr = ":" + p
}
fmt.Println("Server running on", addr)
log.Fatal(http.ListenAndServe(addr, logger(r)))
}
func logger(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("Serving %s", r.URL.String())
h.ServeHTTP(w, r)
log.Printf("Done serving %s [%v]", r.URL.String(), time.Since(start))
})
}
func home(w http.ResponseWriter, r *http.Request) {
data := struct {
Orgs map[string]Organization
Sponsors []Sponsor
Projects map[string]Project
Year int
Config HacktoberfestConfiguration
}{
Orgs: orgs,
Sponsors: sponsors,
Projects: projects,
Year: time.Now().Year(),
Config: hacktoberfestOptions,
}
v.HTML(w, http.StatusOK, "home", data)
}
func dev() bool {
d, err := strconv.ParseBool(os.Getenv("DEV"))
if err != nil {
return false
}
return d
}
func setupDB() error {
var err error
db, err = sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
return errors.Wrap(err, "could not open db")
}
// See if the db is awake. Give it a while to come up with a simple backoff
for attempts := 1; attempts <= 20; attempts++ {
if err = db.Ping(); err == nil {
break
}
log.Printf("Waiting for db to come up, attempt %d of 20: %v", attempts, err)
time.Sleep(time.Duration(attempts) * time.Second)
}
if err != nil {
return errors.Wrap(err, "db never came up")
}
q := `CREATE TABLE IF NOT EXISTS users (
id integer,
username varchar(255),
name varchar(255),
email varchar(255),
avatar varchar(255),
share_info boolean default false,
created_at TIMESTAMP WITH TIME ZONE,
updated_at TIMESTAMP WITH TIME ZONE,
PRIMARY KEY(id)
)`
_, err = db.Exec(q)
if err != nil {
return errors.Wrap(err, "could not make user table")
}
return nil
}