-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add custom template edition w/preview [CLI-135]
- Loading branch information
1 parent
4b8fcb5
commit 8ec6585
Showing
87 changed files
with
6,368 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
//go:generate mockgen -source=branding.go -destination=branding_mock.go -package=auth0 | ||
package auth0 | ||
|
||
import "gopkg.in/auth0.v5/management" | ||
|
||
type BrandingAPI interface { | ||
Read(opts ...management.RequestOption) (b *management.Branding, err error) | ||
UniversalLogin(opts ...management.RequestOption) (ul *management.BrandingUniversalLogin, err error) | ||
SetUniversalLogin(ul *management.BrandingUniversalLogin, opts ...management.RequestOption) (err error) | ||
DeleteUniversalLogin(opts ...management.RequestOption) (err error) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
//go:generate mockgen -source=tenant.go -destination=tenant_mock.go -package=auth0 | ||
package auth0 | ||
|
||
import "gopkg.in/auth0.v5/management" | ||
|
||
type TenantAPI interface { | ||
Read(opts ...management.RequestOption) (t *management.Tenant, err error) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
package branding | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net" | ||
"net/http" | ||
"path/filepath" | ||
"text/template" | ||
"time" | ||
|
||
"github.com/auth0/auth0-cli/internal/open" | ||
"github.com/guiguan/caster" | ||
"github.com/phayes/freeport" | ||
"github.com/rjeczalik/notify" | ||
) | ||
|
||
type Client struct { | ||
Id string `json:"id"` | ||
Name string `json:"name"` | ||
LogoUrl string `json:"logo_url,omitempty"` | ||
} | ||
|
||
type TemplateData struct { | ||
Filename string | ||
Clients []Client | ||
PrimaryColor string | ||
BackgroundColor string | ||
LogoURL string | ||
TenantName string | ||
Body string | ||
} | ||
|
||
func PreviewCustomTemplate(ctx context.Context, templateData TemplateData) { | ||
ctx, cancel := context.WithCancel(ctx) | ||
defer cancel() | ||
|
||
address := "localhost" | ||
port, err := freeport.GetFreePort() | ||
if err != nil { | ||
return | ||
} | ||
|
||
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", address, port)) | ||
if err != nil { | ||
return | ||
} | ||
|
||
requestTimeout := 10 * time.Minute | ||
server := &http.Server{ | ||
Handler: buildRoutes(ctx, requestTimeout, templateData), | ||
ReadTimeout: requestTimeout + 1*time.Minute, | ||
WriteTimeout: requestTimeout + 1*time.Minute, | ||
} | ||
defer server.Close() | ||
|
||
go func() { | ||
if err = server.Serve(listener); err != http.ErrServerClosed { | ||
cancel() | ||
} | ||
}() | ||
|
||
err = open.URL(fmt.Sprintf("http://%s:%d/data/storybook/?path=/story/universal-login--prompts", address, port)) | ||
if err == nil { | ||
return | ||
} | ||
|
||
// Wait until the file is closed or input is cancelled | ||
<-ctx.Done() | ||
} | ||
|
||
func buildRoutes(ctx context.Context, requestTimeout time.Duration, templateData TemplateData) *http.ServeMux { | ||
router := http.NewServeMux() | ||
|
||
// Long polling waiting for file changes | ||
broadcaster := broadcastCustomTemplateChanges(ctx, templateData.Filename) | ||
router.HandleFunc("/dynamic/events", func(w http.ResponseWriter, r *http.Request) { | ||
changes, _ := broadcaster.Sub(r.Context(), 1) | ||
defer broadcaster.Unsub(changes) | ||
|
||
var err error | ||
select { | ||
case <-r.Context().Done(): | ||
w.WriteHeader(http.StatusGone) | ||
_, err = w.Write([]byte("410 - Gone")) | ||
case <-time.After(requestTimeout): | ||
w.WriteHeader(http.StatusRequestTimeout) | ||
_, err = w.Write([]byte("408 - Request Timeout")) | ||
case <-changes: | ||
w.WriteHeader(http.StatusOK) | ||
_, err = w.Write([]byte("200 - OK")) | ||
} | ||
|
||
if err != nil { | ||
http.Error(w, err.Error(), 500) | ||
} | ||
}) | ||
|
||
// The template file | ||
router.HandleFunc("/dynamic/template", func(w http.ResponseWriter, r *http.Request) { | ||
http.ServeFile(w, r, templateData.Filename) | ||
}) | ||
|
||
jstmpl := template.Must(template.New("tenant-data.js").Funcs(template.FuncMap{ | ||
"asJS": func(v interface{}) string { | ||
a, _ := json.Marshal(v) | ||
return string(a) | ||
}, | ||
}).ParseFS(tenantDataAsset, "data/tenant-data.js")) | ||
|
||
router.HandleFunc("/dynamic/tenant-data", func(w http.ResponseWriter, r *http.Request) { | ||
err := jstmpl.Execute(w, templateData) | ||
if err != nil { | ||
http.Error(w, err.Error(), 500) | ||
} | ||
}) | ||
|
||
// Storybook assets | ||
router.Handle("/", http.FileServer(http.FS(templatePreviewAssets))) | ||
|
||
return router | ||
} | ||
|
||
func broadcastCustomTemplateChanges(ctx context.Context, filename string) *caster.Caster { | ||
publisher := caster.New(ctx) | ||
|
||
dir, file := filepath.Split(filename) | ||
c := make(chan notify.EventInfo) | ||
if err := notify.Watch(dir, c, notify.Write); err != nil { | ||
return publisher | ||
} | ||
|
||
go func() { | ||
for eventInfo := range c { | ||
if filepath.Base(eventInfo.Path()) == file { | ||
publisher.Pub(true) | ||
} | ||
} | ||
}() | ||
|
||
// release resources when the file is closed or the input is cancelled | ||
go func() { | ||
<-ctx.Done() | ||
notify.Stop(c) | ||
close(c) | ||
}() | ||
|
||
return publisher | ||
} | ||
|
||
func DefaultTemplate() string { | ||
return defaultTemplate | ||
} | ||
|
||
func FooterTemplate() string { | ||
return footerTemplate | ||
} | ||
|
||
func ImageTemplate() string { | ||
return imageTemplate | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package branding | ||
|
||
import ( | ||
"embed" | ||
_ "embed" | ||
) | ||
|
||
var ( | ||
//go:embed data/storybook/* | ||
templatePreviewAssets embed.FS | ||
|
||
//go:embed data/tenant-data.js | ||
tenantDataAsset embed.FS | ||
|
||
//go:embed data/default-template.liquid | ||
defaultTemplate string | ||
|
||
//go:embed data/footer-template.liquid | ||
footerTemplate string | ||
|
||
//go:embed data/image-template.liquid | ||
imageTemplate string | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
{%- auth0:head -%} | ||
</head> | ||
<body> | ||
{%- auth0:widget -%} | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<!DOCTYPE html> | ||
<html lang="{{locale}}"> | ||
<head> | ||
{%- auth0:head -%} | ||
<style> | ||
body { | ||
background-image: radial-gradient(white, rgb(200, 200, 200)); | ||
} | ||
.footer { | ||
background-color: rgb(120, 120, 120); | ||
position: absolute; | ||
bottom: 0; | ||
left: 0; | ||
padding: 16px 0; | ||
width: 100%; | ||
color: white; | ||
/* Use a high z-index for future-proofing */ | ||
z-index: 10; | ||
} | ||
.footer ul { | ||
text-align: center; | ||
} | ||
.footer ul li { | ||
display: inline-block; | ||
margin: 0 4px; | ||
} | ||
.footer ul li:not(:first-of-type) { | ||
margin-left: 0; | ||
} | ||
.footer ul li:not(:first-of-type)::before { | ||
content: ''; | ||
display: inline-block; | ||
vertical-align: middle; | ||
width: 4px; | ||
height: 4px; | ||
margin-right: 4px; | ||
background-color: white; | ||
border-radius: 50%; | ||
} | ||
.footer a { | ||
color: white; | ||
} | ||
</style> | ||
<title>{{ prompt.screen.texts.pageTitle }}</title> | ||
</head> | ||
<body class="_widget-auto-layout"> | ||
{%- auth0:widget -%} | ||
<footer class="footer"> | ||
<ul> | ||
<li><a href="https://company.com/privacy">Privacy Policy</a></li> | ||
<li><a href="https://company.com/terms">Terms of Service</a></li> | ||
</ul> | ||
</footer> | ||
</body> | ||
</html> |
Oops, something went wrong.