diff --git a/api.go b/app.go similarity index 51% rename from api.go rename to app.go index 90a6404..f93b132 100644 --- a/api.go +++ b/app.go @@ -12,9 +12,15 @@ type Route struct { Handler http.HandlerFunc } -type API struct { - Routes []Route - StaticRoot string +type BackgroundTask struct { + Handler func() + Interval time.Duration +} + +type App struct { + Routes []Route + BackgroundTasks []BackgroundTask + StaticRoot string } func LoggingMiddleware(f http.HandlerFunc) http.HandlerFunc { @@ -25,7 +31,7 @@ func LoggingMiddleware(f http.HandlerFunc) http.HandlerFunc { } } -func (a API) Start(addr string) { +func (a App) Start(addr string) { for _, route := range a.Routes { http.HandleFunc(route.Path, LoggingMiddleware(route.Handler)) } @@ -34,9 +40,28 @@ func (a API) Start(addr string) { http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(a.StaticRoot)))) } + for _, task := range a.BackgroundTasks { + go func() { + for { + task.Handler() + time.Sleep(task.Interval) + } + }() + } + http.ListenAndServe(addr, nil) } -func (a *API) AddRoute(path string, handler http.HandlerFunc) { +func (a *App) AddRoute(path string, handler http.HandlerFunc) { a.Routes = append(a.Routes, Route{Path: path, Handler: handler}) } + +func NewApp(routes map[string]http.HandlerFunc, backgroundTasks []BackgroundTask, staticRoot string) *App { + app := App{StaticRoot: staticRoot, BackgroundTasks: backgroundTasks} + + for route, handler := range routes { + app.AddRoute(route, handler) + } + + return &app +} diff --git a/main.go b/main.go index 29e7747..214430c 100644 --- a/main.go +++ b/main.go @@ -1,86 +1,12 @@ package main import ( - "encoding/json" - "fmt" "net/http" - "net/url" - "text/template" "time" ) var SharedCache *Datastore -type Link struct { - Url string `json:"url"` - PublishedDate string `json:"publishedDate"` - Title string `json:"title"` -} - -// Healthcheck route -// -// Confirms that the app is alive without guarantees -// about it being fully-functional. -func healthcheck(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(200) -} - -// About page -// -// Static content for the about page. -func about(w http.ResponseWriter, r *http.Request) { - tmpl, _ := template.New("about.html.tmpl").ParseFiles("templates/about.html.tmpl") - tmpl.Execute(w, nil) -} - -// Feeds list -// -// Lists all feed elements in store. -func listContent(w http.ResponseWriter, r *http.Request) { - links := []Link{} - for _, feed := range SharedCache.List("feeds") { - var formattedItems []Link - json.Unmarshal([]byte(feed), &formattedItems) - links = append(links, formattedItems...) - } - - tmpl, _ := template.New("index.html.tmpl").ParseFiles("templates/index.html.tmpl") - tmpl.Execute(w, links) -} - -// Manage content -// -// Interface to add new feed subscriptions and get a list -// of current subs. -func manageContent(w http.ResponseWriter, r *http.Request) { - if r.Method == "POST" { - r.ParseForm() - - urlValue := r.PostFormValue("url") - - if _, err := url.Parse(urlValue); err != nil { - w.WriteHeader(400) - return - } - - cacheKey := fmt.Sprintf("feedurl:%s", urlValue) - SharedCache.Set(cacheKey, urlValue) - - go fetchFeed(urlValue) - - w.WriteHeader(201) - } - - allFeeds := SharedCache.List("feedurl") - - type ManageTmplData struct { - Feeds map[string]string - } - - tmpl, _ := template.New("manage.html.tmpl").ParseFiles("templates/manage.html.tmpl") - tmpl.Execute(w, ManageTmplData{Feeds: allFeeds}) -} - var routeMap = map[string]http.HandlerFunc{ "/": listContent, "/about": about, @@ -88,6 +14,10 @@ var routeMap = map[string]http.HandlerFunc{ "/ping": healthcheck, } +var bgTasks = []BackgroundTask{ + {refreshFeeds, 10 * time.Minute}, +} + func main() { SharedCache = &Datastore{ Data: map[string]string{}, @@ -97,18 +27,5 @@ func main() { SharedCache = existingStore } - api := API{StaticRoot: "./static"} - - for route, handler := range routeMap { - api.AddRoute(route, handler) - } - - go func() { - for { - refreshFeeds() - time.Sleep(10 * time.Minute) - } - }() - - api.Start(":9000") + NewApp(routeMap, bgTasks, "./static").Start(":9000") } diff --git a/routes.go b/routes.go new file mode 100644 index 0000000..fd84f12 --- /dev/null +++ b/routes.go @@ -0,0 +1,79 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "text/template" +) + +type Link struct { + Url string `json:"url"` + PublishedDate string `json:"publishedDate"` + Title string `json:"title"` +} + +// Healthcheck route +// +// Confirms that the app is alive without guarantees +// about it being fully-functional. +func healthcheck(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) +} + +// About page +// +// Static content for the about page. +func about(w http.ResponseWriter, r *http.Request) { + tmpl, _ := template.New("about.html.tmpl").ParseFiles("templates/about.html.tmpl") + tmpl.Execute(w, nil) +} + +// Feeds list +// +// Lists all feed elements in store. +func listContent(w http.ResponseWriter, r *http.Request) { + links := []Link{} + for _, feed := range SharedCache.List("feeds") { + var formattedItems []Link + json.Unmarshal([]byte(feed), &formattedItems) + links = append(links, formattedItems...) + } + + tmpl, _ := template.New("index.html.tmpl").ParseFiles("templates/index.html.tmpl") + tmpl.Execute(w, links) +} + +// Manage content +// +// Interface to add new feed subscriptions and get a list +// of current subs. +func manageContent(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + r.ParseForm() + + urlValue := r.PostFormValue("url") + + if _, err := url.Parse(urlValue); err != nil { + w.WriteHeader(400) + return + } + + cacheKey := fmt.Sprintf("feedurl:%s", urlValue) + SharedCache.Set(cacheKey, urlValue) + + go fetchFeed(urlValue) + + w.WriteHeader(201) + } + + allFeeds := SharedCache.List("feedurl") + + type ManageTmplData struct { + Feeds map[string]string + } + + tmpl, _ := template.New("manage.html.tmpl").ParseFiles("templates/manage.html.tmpl") + tmpl.Execute(w, ManageTmplData{Feeds: allFeeds}) +}