User signup validation
parent
687d2940c2
commit
d59ae57c76
@ -1,75 +1,31 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"database/sql"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.runcible.io/learning/ratchet/internal/apperror"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
|
Name string
|
||||||
// User prefered name and email
|
Email string
|
||||||
Name string `json:"name"`
|
HashedPassword []byte
|
||||||
Email string `json:"email"`
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
// Randomly generated API key for use with the API
|
|
||||||
// "-" omits the key from serialization
|
|
||||||
APIKey string `json:"-"`
|
|
||||||
|
|
||||||
// Timestamps for user creatation and last update
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
|
|
||||||
// List of associated Oauth authentication Objects
|
|
||||||
// Not yet implemented
|
|
||||||
// Auths []*Auth `json:"auths"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) Validate() error {
|
type UserService struct {
|
||||||
if u.Name == "" {
|
DB *sql.DB
|
||||||
return apperror.Errorf(apperror.EINVALID, "User name required.")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserService represents a service for managing users.
|
func (u *UserService) Insert(name, email, password string) (int, error) {
|
||||||
type UserService interface {
|
return 0, nil
|
||||||
// Retrieves a user by ID along with their associated auth objects
|
|
||||||
// Returns ENOTFOUND if user does not exist.
|
|
||||||
FindUserByID(ctx context.Context, id int) (*User, error)
|
|
||||||
|
|
||||||
// Retrieves a list of users by filter. Also returns total count of matching users
|
|
||||||
// which may differ from retruned results if filter.Limit is specified.
|
|
||||||
FindUsers(ctc context.Context, filter UserFilter) ([]*User, int, error)
|
|
||||||
|
|
||||||
// Creates a new use. This is only used for testing since users are typically
|
|
||||||
// cretaed during the OAuth creation process in the AuthService.CreateAuth().
|
|
||||||
CreateUser(ctx context.Context, user *User) error
|
|
||||||
|
|
||||||
// Updates a user object. Returns EUNAUTHORIZED if current user is not
|
|
||||||
// the user that is being updated. Returns ENOTFOUND if the user does not exist.
|
|
||||||
UpdateUser(ctx context.Context, id int, upd UserUpdate) (*User, error)
|
|
||||||
|
|
||||||
// Permanently deletes a user and all owned application resources. Returns EUNAUTHORIZED
|
|
||||||
// if current user is not the user being deleted. Returns ENOTFOUND if the user
|
|
||||||
// does not exist.
|
|
||||||
DeleteUser(ctx context.Context, id int) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserFilter respresents a filter passed to FindUsers().
|
func (u *UserService) Authenticate(email, password string) (int, error) {
|
||||||
type UserFilter struct {
|
return 0, nil
|
||||||
ID *int `json:"id"`
|
|
||||||
Email *string `json:"email"`
|
|
||||||
APIKey *string `json:"apiKey"`
|
|
||||||
|
|
||||||
// Restrict to subset of results
|
|
||||||
Offset int `json:"offset"`
|
|
||||||
Limit int `json:"limit"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserUpdate struct {
|
func (u *UserService) Exists(id int) (bool, error) {
|
||||||
Name *string `json:"name"`
|
return false, nil
|
||||||
Email *string `json:"email"`
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.runcible.io/learning/ratchet/internal/apperror"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Userwtf struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
|
||||||
|
// User prefered name and email
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
|
||||||
|
// Randomly generated API key for use with the API
|
||||||
|
// "-" omits the key from serialization
|
||||||
|
APIKey string `json:"-"`
|
||||||
|
|
||||||
|
// Timestamps for user creatation and last update
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
|
||||||
|
// List of associated Oauth authentication Objects
|
||||||
|
// Not yet implemented
|
||||||
|
// Auths []*Auth `json:"auths"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Userwtf) Validate() error {
|
||||||
|
if u.Name == "" {
|
||||||
|
return apperror.Errorf(apperror.EINVALID, "User name required.")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserService represents a service for managing users.
|
||||||
|
type UserServicewtf interface {
|
||||||
|
// Retrieves a user by ID along with their associated auth objects
|
||||||
|
// Returns ENOTFOUND if user does not exist.
|
||||||
|
FindUserByID(ctx context.Context, id int) (*Userwtf, error)
|
||||||
|
|
||||||
|
// Retrieves a list of users by filter. Also returns total count of matching users
|
||||||
|
// which may differ from retruned results if filter.Limit is specified.
|
||||||
|
FindUsers(ctc context.Context, filter UserFilter) ([]*Userwtf, int, error)
|
||||||
|
|
||||||
|
// Creates a new use. This is only used for testing since users are typically
|
||||||
|
// cretaed during the OAuth creation process in the AuthService.CreateAuth().
|
||||||
|
CreateUser(ctx context.Context, user *Userwtf) error
|
||||||
|
|
||||||
|
// Updates a user object. Returns EUNAUTHORIZED if current user is not
|
||||||
|
// the user that is being updated. Returns ENOTFOUND if the user does not exist.
|
||||||
|
UpdateUser(ctx context.Context, id int, upd UserUpdate) (*Userwtf, error)
|
||||||
|
|
||||||
|
// Permanently deletes a user and all owned application resources. Returns EUNAUTHORIZED
|
||||||
|
// if current user is not the user being deleted. Returns ENOTFOUND if the user
|
||||||
|
// does not exist.
|
||||||
|
DeleteUser(ctx context.Context, id int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserFilter respresents a filter passed to FindUsers().
|
||||||
|
type UserFilter struct {
|
||||||
|
ID *int `json:"id"`
|
||||||
|
Email *string `json:"email"`
|
||||||
|
APIKey *string `json:"apiKey"`
|
||||||
|
|
||||||
|
// Restrict to subset of results
|
||||||
|
Offset int `json:"offset"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserUpdate struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
Email *string `json:"email"`
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS users;
|
@ -0,0 +1,16 @@
|
|||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
email TEXT NOT NULL UNIQUE,
|
||||||
|
hashed_password TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Add a trigger to keep timestamp updated.
|
||||||
|
CREATE TRIGGER users_update_timestamp
|
||||||
|
AFTER UPDATE ON users
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE id = OLD.id;
|
||||||
|
END;
|
@ -0,0 +1,30 @@
|
|||||||
|
{{ define "title"}}Signup{{end}}
|
||||||
|
|
||||||
|
{{define "main"}}
|
||||||
|
<form action='/user/signup' method='POST' novalidate>
|
||||||
|
<div>
|
||||||
|
<label>Name:</label>
|
||||||
|
{{with .Form.FieldErrors.name}}
|
||||||
|
<label class='error'>{{.}}</label>
|
||||||
|
{{end}}
|
||||||
|
<input type='text' name='name' value='{{.Form.Name}}'>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Email:</label>
|
||||||
|
{{with .Form.FieldErrors.email}}
|
||||||
|
<label class='error'>{{.}}</label>
|
||||||
|
{{end}}
|
||||||
|
<input type='email' name='email' value='{{.Form.Email}}'>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Password:</label>
|
||||||
|
{{with .Form.FieldErrors.password}}
|
||||||
|
<label class='error'>{{.}}</label>
|
||||||
|
{{end}}
|
||||||
|
<input type='password' name='password'>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type='submit' value='Signup'>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
@ -1,6 +1,15 @@
|
|||||||
{{define "nav" -}}
|
{{define "nav" -}}
|
||||||
<nav>
|
<nav>
|
||||||
<a href='/'>Home</a>
|
<div>
|
||||||
<a href='/snippet/create'>Create snippet</a>
|
<a href='/'>Home</a>
|
||||||
|
<a href='/snippet/create'>Create snippet</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href='/user/signup'>Signup</a>
|
||||||
|
<a href='/user/login'>Login</a>
|
||||||
|
<form action='/user/logout' method='POST'>
|
||||||
|
<button>Logout</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
{{- end}}
|
{{- end}}
|
Loading…
Reference in New Issue