You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-27 00:51:13 +02:00
fix signup validation
This commit is contained in:
@ -40,6 +40,7 @@ func APP(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, staticDir
|
|||||||
// Construct the web.App which holds all routes as well as common Middleware.
|
// Construct the web.App which holds all routes as well as common Middleware.
|
||||||
app := web.NewApp(shutdown, log, env, middlewares...)
|
app := web.NewApp(shutdown, log, env, middlewares...)
|
||||||
|
|
||||||
|
|
||||||
// Register project management pages.
|
// Register project management pages.
|
||||||
p := Projects{
|
p := Projects{
|
||||||
MasterDB: masterDB,
|
MasterDB: masterDB,
|
||||||
@ -63,6 +64,7 @@ func APP(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, staticDir
|
|||||||
app.Handle("GET", "/users/:user_id/update", us.Update, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("GET", "/users/:user_id/update", us.Update, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("GET", "/users/:user_id", us.View, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("GET", "/users/:user_id", us.View, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
|
|
||||||
|
|
||||||
// Register user management and authentication endpoints.
|
// Register user management and authentication endpoints.
|
||||||
u := User{
|
u := User{
|
||||||
MasterDB: masterDB,
|
MasterDB: masterDB,
|
||||||
@ -92,6 +94,7 @@ func APP(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, staticDir
|
|||||||
app.Handle("POST", "/user", u.View, mid.AuthenticateSessionRequired(authenticator), mid.HasAuth())
|
app.Handle("POST", "/user", u.View, mid.AuthenticateSessionRequired(authenticator), mid.HasAuth())
|
||||||
app.Handle("GET", "/user", u.View, mid.AuthenticateSessionRequired(authenticator), mid.HasAuth())
|
app.Handle("GET", "/user", u.View, mid.AuthenticateSessionRequired(authenticator), mid.HasAuth())
|
||||||
|
|
||||||
|
|
||||||
// Register account management endpoints.
|
// Register account management endpoints.
|
||||||
acc := Account{
|
acc := Account{
|
||||||
MasterDB: masterDB,
|
MasterDB: masterDB,
|
||||||
@ -103,6 +106,7 @@ func APP(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, staticDir
|
|||||||
app.Handle("POST", "/account", acc.View, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("POST", "/account", acc.View, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("GET", "/account", acc.View, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("GET", "/account", acc.View, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
|
|
||||||
|
|
||||||
// Register user management and authentication endpoints.
|
// Register user management and authentication endpoints.
|
||||||
s := Signup{
|
s := Signup{
|
||||||
MasterDB: masterDB,
|
MasterDB: masterDB,
|
||||||
@ -121,6 +125,7 @@ func APP(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, staticDir
|
|||||||
app.Handle("GET", "/examples/flash-messages", ex.FlashMessages)
|
app.Handle("GET", "/examples/flash-messages", ex.FlashMessages)
|
||||||
app.Handle("GET", "/examples/images", ex.Images)
|
app.Handle("GET", "/examples/images", ex.Images)
|
||||||
|
|
||||||
|
|
||||||
// Register geo
|
// Register geo
|
||||||
g := Geo{
|
g := Geo{
|
||||||
MasterDB: masterDB,
|
MasterDB: masterDB,
|
||||||
@ -131,6 +136,7 @@ func APP(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, staticDir
|
|||||||
app.Handle("GET", "/geo/geonames/postal_code/:postalCode", g.GeonameByPostalCode)
|
app.Handle("GET", "/geo/geonames/postal_code/:postalCode", g.GeonameByPostalCode)
|
||||||
app.Handle("GET", "/geo/country/:countryCode/timezones", g.CountryTimezones)
|
app.Handle("GET", "/geo/country/:countryCode/timezones", g.CountryTimezones)
|
||||||
|
|
||||||
|
|
||||||
// Register root
|
// Register root
|
||||||
r := Root{
|
r := Root{
|
||||||
MasterDB: masterDB,
|
MasterDB: masterDB,
|
||||||
@ -146,6 +152,7 @@ func APP(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, staticDir
|
|||||||
app.Handle("GET", "/index.html", r.IndexHtml)
|
app.Handle("GET", "/index.html", r.IndexHtml)
|
||||||
app.Handle("GET", "/robots.txt", r.RobotTxt)
|
app.Handle("GET", "/robots.txt", r.RobotTxt)
|
||||||
|
|
||||||
|
|
||||||
// Register health check endpoint. This route is not authenticated.
|
// Register health check endpoint. This route is not authenticated.
|
||||||
check := Check{
|
check := Check{
|
||||||
MasterDB: masterDB,
|
MasterDB: masterDB,
|
||||||
@ -154,6 +161,7 @@ func APP(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, staticDir
|
|||||||
}
|
}
|
||||||
app.Handle("GET", "/v1/health", check.Health)
|
app.Handle("GET", "/v1/health", check.Health)
|
||||||
|
|
||||||
|
|
||||||
// Handle static files/pages. Render a custom 404 page when file not found.
|
// Handle static files/pages. Render a custom 404 page when file not found.
|
||||||
static := func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
static := func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
err := web.StaticHandler(ctx, w, r, params, staticDir, "")
|
err := web.StaticHandler(ctx, w, r, params, staticDir, "")
|
||||||
|
@ -112,7 +112,7 @@ func (h *Signup) Step1(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
|
|
||||||
data["form"] = req
|
data["form"] = req
|
||||||
|
|
||||||
if verr, ok := weberror.NewValidationError(ctx, webcontext.Validator().Struct(signup.SignupRequest{})); ok {
|
if verr, ok := weberror.NewValidationError(ctx, signup.Validator().Struct(signup.SignupRequest{})); ok {
|
||||||
data["validationDefaults"] = verr.(*weberror.Error)
|
data["validationDefaults"] = verr.(*weberror.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
83
cmd/web-app/templates/content/users-update.gohtml
Normal file
83
cmd/web-app/templates/content/users-update.gohtml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{{define "title"}}Update Profile{{end}}
|
||||||
|
{{define "style"}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
{{define "content"}}
|
||||||
|
<form class="user" method="post" novalidate>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputFirstName">First Name</label>
|
||||||
|
<input type="text" class="form-control {{ ValidationFieldClass $.validationErrors "FirstName" }}" placeholder="enter first name" name="FirstName" value="{{ .form.FirstName }}" required>
|
||||||
|
{{template "invalid-feedback" dict "validationDefaults" $.userValidationDefaults "validationErrors" $.validationErrors "fieldName" "FirstName" }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputLastName">Last Name</label>
|
||||||
|
<input type="text" class="form-control {{ ValidationFieldClass $.validationErrors "LastName" }}" placeholder="enter last name" name="LastName" value="{{ .form.LastName }}" required>
|
||||||
|
{{template "invalid-feedback" dict "validationDefaults" $.userValidationDefaults "validationErrors" $.validationErrors "fieldName" "LastName" }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputEmail">Email</label>
|
||||||
|
<input type="text" class="form-control {{ ValidationFieldClass $.validationErrors "Email" }}" placeholder="enter email" name="Email" value="{{ .form.Email }}" required>
|
||||||
|
{{template "invalid-feedback" dict "validationDefaults" $.userValidationDefaults "validationErrors" $.validationErrors "fieldName" "Email" }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputTimezone">Timezone</label>
|
||||||
|
<select class="form-control {{ ValidationFieldClass $.validationErrors "Timezone" }}" name="Timezone">
|
||||||
|
<option value="">Not set</option>
|
||||||
|
{{ range $idx, $t := .timezones }}
|
||||||
|
<option value="{{ $t }}" {{ if CmpString $t $.form.Timezone }}selected="selected"{{ end }}>{{ $t }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
{{template "invalid-feedback" dict "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors "fieldName" "Timezone" }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4 class="card-title">Change Password</h4>
|
||||||
|
<p><small><b>Optional</b>. You can change your password by specifying a new one below. Otherwise leave the fields empty.</small></p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputPassword">Password</label>
|
||||||
|
<input type="password" class="form-control" id="inputPassword" placeholder="" name="Password" value="">
|
||||||
|
<span class="help-block "><small><a a href="javascript:void(0)" id="btnGeneratePassword"><i class="fal fa-random"></i>Generate random password </a></small></span>
|
||||||
|
{{template "invalid-feedback" dict "validationDefaults" $.passwordValidationDefaults "validationErrors" $.validationErrors "fieldName" "Password" }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputPasswordConfirm">Confirm Password</label>
|
||||||
|
<input type="password" class="form-control" id="inputPasswordConfirm" placeholder="" name="PasswordConfirm" value="">
|
||||||
|
{{template "invalid-feedback" dict "validationDefaults" $.passwordValidationDefaults "validationErrors" $.validationErrors "fieldName" "PasswordConfirm" }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="spacer-30"></div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<input id="btnSubmit" type="submit" name="action" value="Save" class="btn btn-primary"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
{{define "js"}}
|
||||||
|
<script>
|
||||||
|
function randomPassword(length) {
|
||||||
|
var chars = "abcdefghijklmnopqrstuvwxyz!@#&*()-+<>ABCDEFGHIJKLMNOP1234567890";
|
||||||
|
var pass = "";
|
||||||
|
for (var x = 0; x < length; x++) {
|
||||||
|
var i = Math.floor(Math.random() * chars.length);
|
||||||
|
pass += chars.charAt(i);
|
||||||
|
}
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#btnGeneratePassword").on("click", function() {
|
||||||
|
pwd = randomPassword(12);
|
||||||
|
$("#inputPassword").attr('type', 'text').val(pwd)
|
||||||
|
$("#inputPasswordConfirm").attr('type', 'text').val(pwd)
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{end}}
|
85
cmd/web-app/templates/content/users-view.gohtml
Normal file
85
cmd/web-app/templates/content/users-view.gohtml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
{{define "title"}}Profile{{end}}
|
||||||
|
{{define "style"}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
{{define "content"}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
|
<img src="{{ .user.Gravatar.Medium }}" alt="gravatar image" class="rounded">
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<h4>Name</h4>
|
||||||
|
<p class="font-14">
|
||||||
|
{{ .user.Name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="spacer-10"></div>
|
||||||
|
<p class="font-10"><a href="https://gravatar.com" target="_blank">Update Avatar</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<a href="/user/update" class="btn btn-outline-success"><i class="fal fa-edit"></i>Edit Details</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="spacer-30"></div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p>
|
||||||
|
<small>Name</small><br/>
|
||||||
|
<b>{{ .user.Name }}</b>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<small>Email</small><br/>
|
||||||
|
<b>{{ .user.Email }}</b>
|
||||||
|
</p>
|
||||||
|
{{if .user.Timezone }}
|
||||||
|
<p>
|
||||||
|
<small>Timezone</small><br/>
|
||||||
|
<b>{{.user.Timezone }}</b>
|
||||||
|
</p>
|
||||||
|
{{end}}
|
||||||
|
<div class="spacer-15"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p>
|
||||||
|
<small>Role</small><br/>
|
||||||
|
{{ if .userAccount }}
|
||||||
|
<b>
|
||||||
|
{{ range $r := .userAccount.Roles }}
|
||||||
|
{{ if eq $r "admin" }}
|
||||||
|
<span class="text-pink-dark"><i class="far fa-user-astronaut"></i>{{ $r }}</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="text-purple-dark"><i class="fal fa-user"></i>{{ $r }}</span>
|
||||||
|
{{end}}
|
||||||
|
{{ end }}
|
||||||
|
</b>
|
||||||
|
{{ end }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<small>Status</small><br/>
|
||||||
|
{{ if .userAccount }}
|
||||||
|
<b>
|
||||||
|
{{ if eq .userAccount.Status.Value "active" }}
|
||||||
|
<span class="text-green"><i class="fas fa-circle"></i>{{ .userAccount.Status.Title }}</span>
|
||||||
|
{{ else if eq .userAccount.Status.Value "invited" }}
|
||||||
|
<span class="text-blue"><i class="fas fa-unicorn"></i>{{ .userAccount.Status.Title }}</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="text-orange"><i class="far fa-circle"></i>{{.userAccount.Status.Title }}</span>
|
||||||
|
{{end}}
|
||||||
|
</b>
|
||||||
|
{{ end }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<small>ID</small><br/>
|
||||||
|
<b>{{ .user.ID }}</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{define "js"}}
|
||||||
|
|
||||||
|
{{end}}
|
@ -2,8 +2,8 @@ package user_account
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
|
||||||
|
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
||||||
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
// UserFindByAccount lists all the users for a given account ID.
|
// UserFindByAccount lists all the users for a given account ID.
|
||||||
func UserFindByAccount(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserFindByAccountRequest) (Users, error) {
|
func UserFindByAccount(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req UserFindByAccountRequest) (Users, error) {
|
||||||
span, ctx := tracer.StartSpanFromContext(ctx, "internal.user_account.UserFind")
|
span, ctx := tracer.StartSpanFromContext(ctx, "internal.user_account.UserFindByAccount")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
||||||
v := webcontext.Validator()
|
v := webcontext.Validator()
|
||||||
|
Reference in New Issue
Block a user