User signup validation
parent
687d2940c2
commit
d59ae57c76
@ -1,75 +1,31 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"git.runcible.io/learning/ratchet/internal/apperror"
|
||||
)
|
||||
|
||||
type User 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"`
|
||||
ID int
|
||||
Name string
|
||||
Email string
|
||||
HashedPassword []byte
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (u *User) Validate() error {
|
||||
if u.Name == "" {
|
||||
return apperror.Errorf(apperror.EINVALID, "User name required.")
|
||||
}
|
||||
return nil
|
||||
type UserService struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
// UserService represents a service for managing users.
|
||||
type UserService 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) (*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
|
||||
func (u *UserService) Insert(name, email, password string) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// 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"`
|
||||
func (u *UserService) Authenticate(email, password string) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
type UserUpdate struct {
|
||||
Name *string `json:"name"`
|
||||
Email *string `json:"email"`
|
||||
func (u *UserService) Exists(id int) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -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" -}}
|
||||
<nav>
|
||||
<a href='/'>Home</a>
|
||||
<a href='/snippet/create'>Create snippet</a>
|
||||
<div>
|
||||
<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>
|
||||
{{- end}}
|
Loading…
Reference in New Issue