Commit 24dfa554 by Torkel Ödegaard

feat(signup): progress on new sign up and email verification flow, #2353

parent d25624a8
......@@ -43,7 +43,8 @@ func Register(r *macaron.Macaron) {
// sign up
r.Get("/signup", Index)
r.Post("/api/user/signup", bind(m.CreateUserCommand{}), wrap(SignUp))
r.Post("/api/user/signup", bind(dtos.SignUpForm{}), wrap(SignUp))
r.Post("/api/user/signup/step2", bind(dtos.SignUpStep2Form{}), wrap(SignUpStep2))
// invited
r.Get("/api/user/invite/:code", wrap(GetInviteInfoByCode))
......
......@@ -4,6 +4,14 @@ type SignUpForm struct {
Email string `json:"email" binding:"Required"`
}
type SignUpStep2Form struct {
Email string `json:"email"`
Name string `json:"name"`
Username string `json:"username"`
Code string `json:"code"`
OrgName string `json:"orgName"`
}
type AdminCreateUserForm struct {
Email string `json:"email"`
Login string `json:"login"`
......
......@@ -27,7 +27,7 @@ func SignUp(c *middleware.Context, form dtos.SignUpForm) Response {
cmd.Email = form.Email
cmd.Status = m.TmpUserSignUpStarted
cmd.InvitedByUserId = c.UserId
cmd.Code = util.GetRandomString(10)
cmd.Code = util.GetRandomString(20)
cmd.RemoteAddr = c.Req.RemoteAddr
if err := bus.Dispatch(&cmd); err != nil {
......@@ -36,13 +36,41 @@ func SignUp(c *middleware.Context, form dtos.SignUpForm) Response {
// user := cmd.Resu
bus.Publish(&events.UserSignedUp{Email: form.Email})
bus.Publish(&events.SignUpStarted{
Email: form.Email,
Code: cmd.Code,
})
//
// loginUserWithUser(&user, c)
//
//
metrics.M_Api_User_SignUpStarted.Inc(1)
return ApiSuccess("User created and logged in")
return Json(200, util.DynMap{"status": "SignUpCreated"})
}
func SignUpStep2(c *middleware.Context, form dtos.SignUpStep2Form) Response {
if !setting.AllowUserSignUp {
return ApiError(401, "User signup is disabled", nil)
}
query := m.GetTempUserByCodeQuery{Code: form.Code}
if err := bus.Dispatch(&query); err != nil {
if err == m.ErrTempUserNotFound {
return ApiError(404, "Invalid email verification code", nil)
}
return ApiError(500, "Failed to read temp user", err)
}
tempUser := query.Result
if tempUser.Email != form.Email {
return ApiError(404, "Email verification code does not match email", nil)
}
existing := m.GetUserByLoginQuery{LoginOrEmail: tempUser.Email}
if err := bus.Dispatch(&existing); err == nil {
return ApiError(401, "User with same email address already exists", nil)
}
return Json(200, util.DynMap{"status": "SignUpCreated"})
}
......@@ -70,12 +70,10 @@ type UserCreated struct {
Email string `json:"email"`
}
type UserSignedUp struct {
type SignUpStarted struct {
Timestamp time.Time `json:"timestamp"`
Id int64 `json:"id"`
Name string `json:"name"`
Login string `json:"login"`
Email string `json:"email"`
Code string `json:"code"`
}
type SignUpCompleted struct {
......
......@@ -25,7 +25,7 @@ func Init() error {
bus.AddHandler("email", validateResetPasswordCode)
bus.AddHandler("email", sendEmailCommandHandler)
bus.AddEventListener(userSignedUpHandler)
bus.AddEventListener(signUpStartedHandler)
mailTemplates = template.New("name")
mailTemplates.Funcs(template.FuncMap{
......@@ -120,7 +120,7 @@ func validateResetPasswordCode(query *m.ValidateResetPasswordCodeQuery) error {
return nil
}
func userSignedUpHandler(evt *events.UserSignedUp) error {
func signUpStartedHandler(evt *events.SignUpStarted) error {
log.Info("User signed up: %s, send_option: %s", evt.Email, setting.Smtp.SendWelcomeEmailOnSignUp)
if evt.Email == "" || !setting.Smtp.SendWelcomeEmailOnSignUp {
......
......@@ -7,6 +7,7 @@ define([
'./jsonEditorCtrl',
'./loginCtrl',
'./invitedCtrl',
'./signupCtrl',
'./resetPasswordCtrl',
'./sidemenuCtrl',
'./errorCtrl',
......
......@@ -58,8 +58,12 @@ function (angular, config) {
return;
}
backendSrv.post('/api/user/signup', $scope.formModel).then(function() {
window.location.href = config.appSubUrl + '/';
backendSrv.post('/api/user/signup', $scope.formModel).then(function(result) {
if (result.status === 'SignUpCreated') {
$location.path('/signup').search({email: $scope.formModel.email});
} else {
window.location.href = config.appSubUrl + '/';
}
});
};
......
define([
'angular',
'config',
],
function (angular, config) {
'use strict';
var module = angular.module('grafana.controllers');
module.controller('SignUpCtrl', function($scope, $location, contextSrv, backendSrv) {
contextSrv.sidemenu = false;
$scope.formModel = {};
$scope.init = function() {
var email = $location.search().email;
$scope.formModel.orgName = email;
$scope.formModel.email = email;
$scope.formModel.username = email;
};
$scope.submit = function() {
if (!$scope.signupForm.$valid) {
return;
}
backendSrv.post('/api/user/signup/step2', $scope.formModel).then(function() {
window.location.href = config.appSubUrl + '/';
});
};
$scope.init();
});
});
<div class="container">
<div class="signup-page-background">
</div>
<div class="login-box">
<div class="login-box-logo">
<img src="img/logo_transparent_200x75.png">
</div>
<div class="invite-box">
<h3>
You're almost there.
</h3>
<div class="modal-tagline">
We just need a couple of more bits of<br> information to finish creating your account.
</div>
<div style="display: inline-block; margin-top: 25px; width: 300px">
<div class="editor-option">
<label class="small">Your email:</label>
<span class="large">{{formModel.email}}</span>
</div>
</div>
<br>
<form name="signupForm" class="login-form">
<div style="display: inline-block; margin-bottom: 25px; width: 300px">
<div class="editor-option">
<label class="small">Email verification code: <em>Sent to your email just now</em></label>
<input type="text" class="input input-xlarge" ng-model="formModel.code" required></input>
</div>
</div>
<div class="tight-from-container">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 128px">
Organization
</li>
<li>
<input type="text" name="orgName" class="tight-form-input last" ng-model='formModel.orgName' placeholder="Name your organization" style="width: 253px">
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 128px">
Name
</li>
<li>
<input type="text" name="name" class="tight-form-input last" ng-model='formModel.name' placeholder="Name (optional)" style="width: 253px">
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 128px">
Username
</li>
<li>
<input type="text" class="tight-form-input last" required ng-model='formModel.username' placeholder="Username" style="width: 253px" autocomplete="off">
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 128px">
Password
</li>
<li>
<input type="password" class="tight-form-input last" required ng-model="formModel.password" id="inputPassword" style="width: 253px" placeholder="password" autocomplete="off">
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
<div style="margin-left: 147px; width: 254px;">
<password-strength password="formModel.password"></password-strength>
</div>
<div class="login-submit-button-row">
<button type="submit" class="btn" ng-click="submit();" ng-class="{'btn-inverse': !signUpForm.$valid, 'btn-primary': signUpForm.$valid}">
Continue
</button>
</div>
</form>
<div class="clearfix"></div>
</div>
<div class="row" style="margin-top: 50px">
<div class="version-footer text-center small">
Grafana version: {{buildInfo.version}}, commit: {{buildInfo.commit}},
build date: {{buildInfo.buildstamp | date: 'yyyy-MM-dd HH:mm:ss' }}
</div>
</div>
</div>
</div>
......@@ -106,6 +106,10 @@ define([
templateUrl: 'app/partials/signup_invited.html',
controller : 'InvitedCtrl',
})
.when('/signup', {
templateUrl: 'app/partials/signup_step2.html',
controller : 'SignUpCtrl',
})
.when('/user/password/send-reset-email', {
templateUrl: 'app/partials/reset_password.html',
controller : 'ResetPasswordCtrl',
......
......@@ -4,7 +4,7 @@
float: left;
margin-left: 25%;
margin-right: 25%;
padding-top: 50px;
padding-top: 25px;
}
.login-box {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment