add web client - future plans for loading screen during refresh
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Steven Polley 2023-11-13 18:05:20 -07:00
parent 0a518fd31a
commit 4c904e7196
5 changed files with 64 additions and 7 deletions

16
main.go
View File

@ -3,6 +3,8 @@ package main
import ( import (
"log" "log"
"os" "os"
"sync"
"text/template"
"time" "time"
"deadbeef.codes/steven/ynab-portfolio-monitor/ynab" "deadbeef.codes/steven/ynab-portfolio-monitor/ynab"
@ -11,7 +13,9 @@ import (
var ( var (
configuredProviders []AccountProvider // Any account providers that are successfully configured get added to this slice configuredProviders []AccountProvider // Any account providers that are successfully configured get added to this slice
ynabClient *ynab.Client // YNAB HTTP client ynabClient *ynab.Client // YNAB HTTP client
lastRefresh time.Time lastRefresh time.Time // Timestamp that last data refresh ran - used for rate limiting
refreshRunning *sync.Mutex // True if a refresh is currently running
t *template.Template // HTML templates in the templates directory
) )
// Called at program startup or if SIGHUP is received // Called at program startup or if SIGHUP is received
@ -49,6 +53,13 @@ func init() {
if err != nil { if err != nil {
log.Fatalf("failed to create ynab client: %v", err) log.Fatalf("failed to create ynab client: %v", err)
} }
// Web Templates
// Parse all template files at startup
t, err = template.ParseGlob("./templates/*")
if err != nil {
log.Fatalf("couldn't parse HTML templates: %v", err)
}
} }
func main() { func main() {
@ -62,10 +73,13 @@ func main() {
} }
func refreshData() { func refreshData() {
refreshRunning.Lock()
defer refreshRunning.Unlock()
// Only allow a refresh at most once every 5 minutes // Only allow a refresh at most once every 5 minutes
if time.Now().Before(lastRefresh.Add(time.Minute * 5)) { if time.Now().Before(lastRefresh.Add(time.Minute * 5)) {
log.Printf("refresh rate limited") log.Printf("refresh rate limited")
return return
} }
lastRefresh = time.Now() lastRefresh = time.Now()

3
public/README.md Normal file
View File

@ -0,0 +1,3 @@
# Public
This directory is publicly accessible from the HTTP server and contains web assets such as images, css and javascript.

3
templates/README.md Normal file
View File

@ -0,0 +1,3 @@
# Templates
This directory contains HTML templates used by the Go templating engine.

22
templates/home.html Normal file
View File

@ -0,0 +1,22 @@
<html>
<head>
<title>YNAB Portfolio Monitor</title>
<script>
req = new XMLHttpRequest();
req.open("GET", "/status");
req.send();
req.onload = () => {
if (req.readyState == 4 && req.status == 200) {
// redirect to budget when refresh complete signal is received
window.location.href = "https://app.ynab.com/{{ .BudgetID }}"
}
};
// do some fancy preloader stuff
</script>
</head>
<body>
<p>Loading...</p>
</body>
</html>

View File

@ -1,13 +1,11 @@
package main package main
import ( import (
"fmt"
"log" "log"
"net/http" "net/http"
) )
func webServer() { func webServer() {
// Start web server
//Public static files //Public static files
http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("public")))) http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("public"))))
@ -15,13 +13,30 @@ func webServer() {
// Page Handlers // Page Handlers
// Anything that is responsible for the base elements of a viewable web page // Anything that is responsible for the base elements of a viewable web page
http.HandleFunc("/", homePageHandler) http.HandleFunc("/", homePageHandler)
http.HandleFunc("/status", statusHandler)
// Start web server
log.Print("Service listening on :8080") log.Print("Service listening on :8080")
log.Printf("Web server unexpectedly exiting!: %v", http.ListenAndServe(":8080", nil)) log.Printf("Web server unexpectedly exiting!: %v", http.ListenAndServe(":8080", nil))
}
type homePageData struct {
BudgetID string
} }
func homePageHandler(w http.ResponseWriter, r *http.Request) { func homePageHandler(w http.ResponseWriter, r *http.Request) {
refreshData() go refreshData()
http.Redirect(w, r, fmt.Sprintf("https://app.ynab.com/%s", ynabClient.BudgetID), http.StatusSeeOther) pageData := &homePageData{BudgetID: ynabClient.BudgetID}
err := t.ExecuteTemplate(w, "home.html", pageData)
if err != nil {
log.Printf("error executing home.html template: %v", err)
}
//http.Redirect(w, r, fmt.Sprintf("https://app.ynab.com/%s", ynabClient.BudgetID), http.StatusSeeOther)
}
func statusHandler(w http.ResponseWriter, r *http.Request) {
refreshRunning.Lock()
w.WriteHeader(http.StatusOK)
refreshRunning.Unlock()
} }