"context"
"encoding/json"
"fmt"
+ "html/template"
"io/ioutil"
"net/http"
"sync"
var delayedHandleNotifications = delay.Func("handleNotifications", handleNotifications)
+var templates *template.Template
+
func init() {
http.Handle("/fitbit/connect", AuthenticatedHandler(fitbitConnectHandler))
http.Handle("/fitbit/grant", AuthenticatedHandler(fitbitGrantHandler))
- http.Handle("/fitbit/notify", ContextHandler(fitbitNotifyHandler))
http.Handle("/fitbit/disconnect", AuthenticatedHandler(fitbitDisconnectHandler))
http.Handle("/google/connect", AuthenticatedHandler(googleConnectHandler))
http.Handle("/google/grant", AuthenticatedHandler(googleGrantHandler))
http.Handle("/google/disconnect", AuthenticatedHandler(googleDisconnectHandler))
- http.Handle("/", AuthenticatedHandler(indexHandler))
+ // unauthenticated
+ http.Handle("/fitbit/notify", ContextHandler(fitbitNotifyHandler))
+ http.Handle("/", ContextHandler(indexHandler))
+
+ t, err := template.ParseGlob("templates/*.html")
+ if err != nil {
+ panic(err)
+ }
+ templates = t
}
// ContextHandler implements http.Handler
}
}
-func indexHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, u *app.User) error {
- _, err := u.Token(ctx, "Fitbit")
- if err != nil && err != datastore.ErrNoSuchEntity {
- return err
- }
- haveFitbitToken := err == nil
-
- _, err = u.Token(ctx, "Google")
- if err != nil && err != datastore.ErrNoSuchEntity {
- return err
+func indexHandler(ctx context.Context, w http.ResponseWriter, _ *http.Request) error {
+ var templateData struct {
+ HaveFitbit bool
+ HaveGoogleFit bool
+ *app.User
}
- haveGoogleToken := err == nil
-
- fmt.Fprintln(w, "<html><head><title>Kraftakt</title></head>")
- fmt.Fprintln(w, "<body><h1>Kraftakt</h1>")
+ templateName := "main.html"
- fmt.Fprintln(w, "<p><strong>Kraftakt</strong> copies your <em>Fitbit</em> data to <em>Google Fit</em>, seconds after you sync.</p>")
+ if gaeUser := user.Current(ctx); gaeUser != nil {
+ templateName = "loggedin.html"
- fmt.Fprintf(w, "<p>Hello %s</p>\n", user.Current(ctx).Email)
- fmt.Fprintln(w, "<ul>")
+ u, err := app.NewUser(ctx, gaeUser.Email)
+ if err != nil {
+ return err
+ }
+ templateData.User = u
- fmt.Fprint(w, "<li>Fitbit: ")
- if haveFitbitToken {
- fmt.Fprint(w, `<strong style="color: DarkGreen;">Authorized</strong>`)
- } else {
- fmt.Fprint(w, `<strong style="color: DarkRed;">Not authorized</strong> (<a href="/fitbit/setup">Authorize</a>)`)
- }
- fmt.Fprintln(w, "</li>")
+ _, err = u.Token(ctx, "Fitbit")
+ if err != nil && err != datastore.ErrNoSuchEntity {
+ return err
+ }
+ templateData.HaveFitbit = (err == nil)
- fmt.Fprint(w, "<li>Google Fit: ")
- if haveGoogleToken {
- fmt.Fprint(w, `<strong style="color: DarkGreen;">Authorized</strong>`)
- } else {
- fmt.Fprint(w, `<strong style="color: DarkRed;">Not authorized</strong> (<a href="/google/setup">Authorize</a>)`)
+ _, err = u.Token(ctx, "Google")
+ if err != nil && err != datastore.ErrNoSuchEntity {
+ return err
+ }
+ templateData.HaveGoogleFit = (err == nil)
}
- fmt.Fprintln(w, "</li>")
- fmt.Fprintln(w, "</ul>")
- fmt.Fprintln(w, "</body></html>")
-
- return nil
+ return templates.ExecuteTemplate(w, templateName, &templateData)
}
func fitbitConnectHandler(_ context.Context, w http.ResponseWriter, r *http.Request, _ *app.User) error {
if err := fitbit.ParseToken(ctx, r, u); err != nil {
return err
}
- c, err := fitbit.NewClient(ctx, "-", u)
+ c, err := fitbit.NewClient(ctx, "", u)
if err != nil {
return err
}
return nil
}
-func googleSetupHandler(w http.ResponseWriter, r *http.Request) {
+func fitbitDisconnectHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, u *app.User) error {
+ c, err := fitbit.NewClient(ctx, "", u)
+ if err != nil {
+ return err
+ }
+
+ var errs appengine.MultiError
+ if err := c.Unsubscribe(ctx); err != nil {
+ errs = append(errs, fmt.Errorf("Unsubscribe() = %v", err))
+ }
+
+ if err := c.DeleteToken(ctx); err != nil {
+ errs = append(errs, fmt.Errorf("DeleteToken() = %v", err))
+ }
+ if len(errs) != 0 {
+ return errs
+ }
+
+ redirectURL := r.URL
+ redirectURL.Path = "/"
+ redirectURL.RawQuery = ""
+ redirectURL.Fragment = ""
+ http.Redirect(w, r, redirectURL.String(), http.StatusTemporaryRedirect)
+ return nil
+}
+
+func googleConnectHandler(_ context.Context, w http.ResponseWriter, r *http.Request, _ *app.User) error {
http.Redirect(w, r, gfit.AuthURL(), http.StatusTemporaryRedirect)
+ return nil
}
func googleGrantHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, u *app.User) error {
return nil
}
+func googleDisconnectHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, u *app.User) error {
+ c, err := gfit.NewClient(ctx, u)
+ if err != nil {
+ return err
+ }
+
+ if err := c.DeleteToken(ctx); err != nil {
+ return err
+ }
+
+ redirectURL := r.URL
+ redirectURL.Path = "/"
+ redirectURL.RawQuery = ""
+ redirectURL.Fragment = ""
+ http.Redirect(w, r, redirectURL.String(), http.StatusTemporaryRedirect)
+ return nil
+}
+
// fitbitNotifyHandler is called by Fitbit whenever there are updates to a
// subscription. It verifies the payload, splits it into individual
// notifications and adds it to the taskqueue service.