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.
|
||||
app := web.NewApp(shutdown, log, env, middlewares...)
|
||||
|
||||
|
||||
// Register project management pages.
|
||||
p := Projects{
|
||||
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", us.View, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
|
||||
|
||||
// Register user management and authentication endpoints.
|
||||
u := User{
|
||||
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("GET", "/user", u.View, mid.AuthenticateSessionRequired(authenticator), mid.HasAuth())
|
||||
|
||||
|
||||
// Register account management endpoints.
|
||||
acc := Account{
|
||||
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("GET", "/account", acc.View, mid.AuthenticateSessionRequired(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||
|
||||
|
||||
// Register user management and authentication endpoints.
|
||||
s := Signup{
|
||||
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/images", ex.Images)
|
||||
|
||||
|
||||
// Register geo
|
||||
g := Geo{
|
||||
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/country/:countryCode/timezones", g.CountryTimezones)
|
||||
|
||||
|
||||
// Register root
|
||||
r := Root{
|
||||
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", "/robots.txt", r.RobotTxt)
|
||||
|
||||
|
||||
// Register health check endpoint. This route is not authenticated.
|
||||
check := Check{
|
||||
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)
|
||||
|
||||
|
||||
// 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 {
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
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 (
|
||||
"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"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
||||
@ -11,7 +11,7 @@ import (
|
||||
|
||||
// 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) {
|
||||
span, ctx := tracer.StartSpanFromContext(ctx, "internal.user_account.UserFind")
|
||||
span, ctx := tracer.StartSpanFromContext(ctx, "internal.user_account.UserFindByAccount")
|
||||
defer span.Finish()
|
||||
|
||||
v := webcontext.Validator()
|
||||
|
Reference in New Issue
Block a user