add web client - future plans for loading screen during refresh
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		
							
								
								
									
										20
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								main.go
									
									
									
									
									
								
							@@ -3,15 +3,19 @@ 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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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
									
								
							
							
						
						
									
										3
									
								
								public/README.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								templates/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					# Templates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This directory contains HTML templates used by the Go templating engine.
 | 
				
			||||||
							
								
								
									
										22
									
								
								templates/home.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								templates/home.html
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										23
									
								
								webServer.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								webServer.go
									
									
									
									
									
								
							@@ -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()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user