account page stubbed: link to etsy sign up

This commit is contained in:
2025-12-29 06:06:13 -07:00
parent 61f9afb39c
commit c9100383e4
35 changed files with 1326 additions and 158 deletions
+127 -9
View File
@@ -7,16 +7,61 @@ import (
"net/http"
"net/url"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/angelbeltran/templater"
"ruben/inventory2/internal/domains/accounts"
etsy_platform "ruben/inventory2/internal/domains/platforms/etsy"
"ruben/inventory2/internal/domains/raw_events"
)
func NewSiteHandler(dir string, db *raw_events.Store) http.Handler {
func NewSiteHandler(
dir string,
rawEvents *raw_events.Store,
accts *accounts.Store,
etsy *etsy_platform.Platform,
) http.Handler {
mux := http.NewServeMux()
// api routes
mux.HandleFunc("POST /accounts", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
email := r.FormValue("email")
if email == "" {
http.Error(w, "no email provided", http.StatusBadRequest)
return
}
acct, err := accts.CreateAccount(ctx, email)
if err != nil {
http.Error(w, fmt.Sprintf("failed to create account: %w", err), http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/site/accounts/%d", acct.ID), http.StatusSeeOther)
})
mux.HandleFunc("POST /log-in", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
email := r.FormValue("email")
if email == "" {
http.Error(w, "no email provided", http.StatusBadRequest)
return
}
acct, err := accts.GetAccountByEmail(ctx, email)
if err != nil {
http.Error(w, fmt.Sprintf("failed to create account: %w", err), http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/site/accounts/%d", acct.ID), http.StatusSeeOther)
})
// non-html routes
scfs := http.FileServer(http.Dir(dir + "/scripts"))
@@ -44,6 +89,12 @@ func NewSiteHandler(dir string, db *raw_events.Store) http.Handler {
// TODO: make "/site" dynamic somehow
return path.Join(append([]string{"/site"}, strParts...)...)
},
"splitPath": func(p string) []string {
if p == "" {
return nil
}
return strings.Split(strings.TrimSuffix(strings.TrimPrefix(p, "/"), "/"), "/")
},
"prettyPrintJSON": func(j json.RawMessage) string {
b, err := json.MarshalIndent(j, " ", "")
@@ -53,6 +104,10 @@ func NewSiteHandler(dir string, db *raw_events.Store) http.Handler {
return string(b)
},
"parseInt64": func(s string) (int64, error) {
return strconv.ParseInt(s, 10, 64)
},
"addInt": func(a, b int) int {
return a + b
},
@@ -66,15 +121,23 @@ func NewSiteHandler(dir string, db *raw_events.Store) http.Handler {
},
)
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
name, pathParams := getPageTemplateNameForURL(r.URL)
b, err := tmplr.ExecutePage(
getPageTemplateNameForURL(r.URL),
name,
"Request",
r,
// add services here
"RawEvents",
db.WithContext(r.Context()),
rawEvents.WithContext(ctx),
"URLCalc",
newURLCalculator(r.URL),
"PathParams",
pathParams,
"Accounts",
accts.WithContext(ctx),
"Etsy",
etsy.WithContext(ctx),
)
if err != nil {
// TODO: handle 'not found' as a 404?
@@ -89,14 +152,69 @@ func NewSiteHandler(dir string, db *raw_events.Store) http.Handler {
return mux
}
func getPageTemplateNameForURL(u *url.URL) string {
filepath := strings.TrimPrefix(strings.TrimSuffix(u.Path, ".html"), "/")
if filepath == "" {
// "/" maps to "/home"
filepath = "home"
// TODO: clean this up...
// TODO: somehow tell what the path params are and pass them up.
// - then consider pushing this functionality into the template library.
//
// getPageTemplateNameForURL eliminate any trailing .html or /, and checks for any
// file with path parameters in the name, eg '{abc}.html.tmpl', prefering exact filename matches.
func getPageTemplateNameForURL(u *url.URL) (name string, params map[string]string) {
fp := strings.TrimPrefix(strings.TrimSuffix(strings.TrimSuffix(u.Path, ".html"), "/"), "/")
if fp == "" {
// "/" maps to "/index"
fp = "index"
}
return filepath
fpParts := strings.Split(fp, "/")
res := getMatchingGlobPatternsCapturingFilepathIncludingParametrizedFilepaths(fpParts)
for _, combs := range res {
const pageBodiesPrefix = "internal/site/templates/page_bodies"
pattern := path.Join(pageBodiesPrefix, path.Join(combs...)) + ".html.tmpl"
matches, _ := filepath.Glob(pattern)
if len(matches) == 0 {
pattern := path.Join(pageBodiesPrefix, path.Join(combs...), "index") + ".html.tmpl"
matches, _ = filepath.Glob(pattern)
}
if len(matches) > 0 {
match := matches[0]
name = strings.TrimPrefix(strings.TrimSuffix(match, ".html.tmpl"), pageBodiesPrefix+"/")
patternParts := strings.Split(name, "/")
params = make(map[string]string)
for i, pp := range patternParts {
if strings.HasPrefix(pp, "{") && strings.HasSuffix(pp, "}") {
params[pp[1:len(pp)-1]] = fpParts[i]
}
}
return name, params
}
}
return fp, nil
}
func getMatchingGlobPatternsCapturingFilepathIncludingParametrizedFilepaths(filepathParts []string) [][]string {
switch len(filepathParts) {
case 0:
return nil
case 1:
return [][]string{
[]string{filepathParts[0]},
[]string{"{*}"},
}
default:
tailCombs := getMatchingGlobPatternsCapturingFilepathIncludingParametrizedFilepaths(filepathParts[1:])
combs := make([][]string, 2*len(tailCombs))
for i, c := range tailCombs {
combs[i*2] = append([]string{filepathParts[0]}, c...)
combs[i*2+1] = append([]string{"{*}"}, c...)
}
return combs
}
}
type URLCalculator struct {
@@ -7,8 +7,14 @@
</li>
<li>
<a href="{{ buildSitePath "reports" }}">
Reports
<a href="{{ buildSitePath "sign-up" }}">
Sign Up
</a>
</li>
<li>
<a href="{{ buildSitePath "log-in" }}">
Log In
</a>
</li>
</ul>
@@ -0,0 +1,20 @@
{{ componentBody "nav_bar" }}
<h1>Account: {{ .PathParams.acctID }}</h1>
{{- $acctID := parseInt64 .PathParams.acctID }}
Email: {{ (.Accounts.GetAccount $acctID).Email }}
{{- $etsyUser := .Etsy.GetUserPointerByAccountID $acctID }}
{{- if $etsyUser }}
<h3>Etsy User: {{ $etsyUser.UserID }}; Shop ID: {{ $etsyUser.ShopID }}</h3>
{{- else }}
<h3>
<a href="{{ .Etsy.GenerateConnectionURLForNewAccount $acctID }}">
Link Your Etsy Store!
</a>
</h3>
{{- end }}
<h2><a href="{{$acctID}}/reports">View Reports</a></h2>
@@ -1,3 +1,5 @@
{{ componentBody "nav_bar" }}
<h1>Home</h1>
<h2><a href="sign-up">Sign Up!</a></h2>
@@ -0,0 +1,11 @@
{{ componentBody "nav_bar" }}
<h1>Log In</h1>
<form action="log-in" method="post">
<label>
Email:
<input type="text" required name="email" />
</label>
<input type="submit" value="login" />
</form>
@@ -0,0 +1,11 @@
{{ componentBody "nav_bar" }}
<h1>Sign Up</h1>
<form action="accounts" method="post">
<label>
Email:
<input type="text" required name="email" />
</label>
<input type="submit" value="Submit" />
</form>