2017-05-06 21:21:39 +02:00
package userService
import (
"errors"
"net/http"
"strconv"
"golang.org/x/crypto/bcrypt"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
2017-05-07 00:10:40 +02:00
formStruct "github.com/ewhal/nyaa/service/user/form"
2017-05-06 21:21:39 +02:00
"github.com/ewhal/nyaa/util/crypto"
"github.com/ewhal/nyaa/util/log"
"github.com/ewhal/nyaa/util/modelHelper"
"github.com/ewhal/nyaa/util/timeHelper"
2017-05-07 21:14:32 +02:00
"fmt"
2017-05-06 21:21:39 +02:00
)
var userFields [ ] string = [ ] string { "name" , "email" , "createdAt" , "updatedAt" }
// SuggestUsername suggest user's name if user's name already occupied.
func SuggestUsername ( username string ) string {
var count int
var usernameCandidate string
db . ORM . Model ( model . User { } ) . Where ( & model . User { Username : username } ) . Count ( & count )
log . Debugf ( "count Before : %d" , count )
if count == 0 {
return username
} else {
var postfix int
for {
usernameCandidate = username + strconv . Itoa ( postfix )
log . Debugf ( "usernameCandidate: %s\n" , usernameCandidate )
db . ORM . Model ( model . User { } ) . Where ( & model . User { Username : usernameCandidate } ) . Count ( & count )
log . Debugf ( "count after : %d\n" , count )
postfix = postfix + 1
if count == 0 {
break
}
}
}
return usernameCandidate
}
2017-05-07 21:14:32 +02:00
func CheckEmail ( email string ) bool {
var count int
db . ORM . Model ( model . User { } ) . Where ( & model . User { Email : email } ) . Count ( & count )
if count == 0 {
return false
}
return true
}
2017-05-06 21:21:39 +02:00
// CreateUserFromForm creates a user from a registration form.
2017-05-07 00:10:40 +02:00
func CreateUserFromForm ( registrationForm formStruct . RegistrationForm ) ( model . User , error ) {
2017-05-06 21:21:39 +02:00
var user model . User
log . Debugf ( "registrationForm %+v\n" , registrationForm )
modelHelper . AssignValue ( & user , & registrationForm )
user . Md5 = crypto . GenerateMD5Hash ( user . Email ) // Gravatar
token , err := crypto . GenerateRandomToken32 ( )
if err != nil {
return user , errors . New ( "Token not generated." )
}
user . Token = token
user . TokenExpiration = timeHelper . FewDaysLater ( config . AuthTokenExpirationDay )
log . Debugf ( "user %+v\n" , user )
if db . ORM . Create ( & user ) . Error != nil {
return user , errors . New ( "User is not created." )
}
return user , nil
}
// CreateUser creates a user.
func CreateUser ( w http . ResponseWriter , r * http . Request ) ( int , error ) {
var user model . User
2017-05-07 00:10:40 +02:00
var registrationForm formStruct . RegistrationForm
2017-05-06 21:21:39 +02:00
var status int
var err error
modelHelper . BindValueForm ( & registrationForm , r )
2017-05-07 21:14:32 +02:00
usernameCandidate := SuggestUsername ( registrationForm . Username )
if ( usernameCandidate != registrationForm . Username ) {
return http . StatusInternalServerError , fmt . Errorf ( "Username already taken, you can choose: %s" , usernameCandidate )
}
if CheckEmail ( registrationForm . Email ) {
return http . StatusInternalServerError , errors . New ( "Email Address already in our database!" )
}
2017-05-06 21:21:39 +02:00
password , err := bcrypt . GenerateFromPassword ( [ ] byte ( registrationForm . Password ) , 10 )
if err != nil {
return http . StatusInternalServerError , err
}
registrationForm . Password = string ( password )
user , err = CreateUserFromForm ( registrationForm )
if err != nil {
return http . StatusInternalServerError , err
}
SendVerificationToUser ( user )
2017-05-06 22:27:21 +02:00
status , err = RegisterHandler ( w , r )
2017-05-06 21:21:39 +02:00
return status , err
}
// RetrieveUser retrieves a user.
func RetrieveUser ( r * http . Request , id string ) ( * model . PublicUser , bool , uint , int , error ) {
var user model . User
var currentUserId uint
var isAuthor bool
// var publicUser *model.PublicUser
// publicUser.User = &user
if db . ORM . Select ( config . UserPublicFields ) . First ( & user , id ) . RecordNotFound ( ) {
return & model . PublicUser { User : & user } , isAuthor , currentUserId , http . StatusNotFound , errors . New ( "User is not found." )
}
currentUser , err := CurrentUser ( r )
if err == nil {
currentUserId = currentUser . Id
isAuthor = currentUser . Id == user . Id
}
var likings [ ] model . User
var likingCount int
db . ORM . Table ( "users_followers" ) . Where ( "users_followers.user_id=?" , user . Id ) . Count ( & likingCount )
2017-05-06 22:14:02 +02:00
if err = db . ORM . Order ( "created_at desc" ) . Select ( config . UserPublicFields ) .
2017-05-06 21:21:39 +02:00
Joins ( "JOIN users_followers on users_followers.user_id=?" , user . Id ) .
Where ( "users.id = users_followers.follower_id" ) .
Group ( "users.id" ) . Find ( & likings ) . Error ; err != nil {
log . Fatal ( err . Error ( ) )
}
user . Likings = likings
var liked [ ] model . User
var likedCount int
db . ORM . Table ( "users_followers" ) . Where ( "users_followers.follower_id=?" , user . Id ) . Count ( & likedCount )
2017-05-06 22:14:02 +02:00
if err = db . ORM . Order ( "created_at desc" ) . Select ( config . UserPublicFields ) .
2017-05-06 21:21:39 +02:00
Joins ( "JOIN users_followers on users_followers.follower_id=?" , user . Id ) .
Where ( "users.id = users_followers.user_id" ) .
Group ( "users.id" ) . Find ( & liked ) . Error ; err != nil {
log . Fatal ( err . Error ( ) )
}
user . Liked = liked
log . Debugf ( "user liking %v\n" , user . Likings )
log . Debugf ( "user liked %v\n" , user . Liked )
return & model . PublicUser { User : & user } , isAuthor , currentUserId , http . StatusOK , nil
}
// RetrieveUsers retrieves users.
func RetrieveUsers ( ) [ ] * model . PublicUser {
var users [ ] * model . User
var userArr [ ] * model . PublicUser
db . ORM . Select ( config . UserPublicFields ) . Find ( & users )
for _ , user := range users {
userArr = append ( userArr , & model . PublicUser { User : user } )
}
return userArr
}
// UpdateUserCore updates a user. (Applying the modifed data of user).
func UpdateUserCore ( user * model . User ) ( int , error ) {
user . Md5 = crypto . GenerateMD5Hash ( user . Email )
token , err := crypto . GenerateRandomToken32 ( )
if err != nil {
return http . StatusInternalServerError , errors . New ( "Token not generated." )
}
user . Token = token
user . TokenExpiration = timeHelper . FewDaysLater ( config . AuthTokenExpirationDay )
if db . ORM . Save ( user ) . Error != nil {
return http . StatusInternalServerError , errors . New ( "User is not updated." )
}
return http . StatusOK , nil
}
// UpdateUser updates a user.
func UpdateUser ( w http . ResponseWriter , r * http . Request , id string ) ( * model . User , int , error ) {
var user model . User
if db . ORM . First ( & user , id ) . RecordNotFound ( ) {
return & user , http . StatusNotFound , errors . New ( "User is not found." )
}
switch r . FormValue ( "type" ) {
case "password" :
2017-05-07 00:10:40 +02:00
var passwordForm formStruct . PasswordForm
2017-05-06 21:21:39 +02:00
modelHelper . BindValueForm ( & passwordForm , r )
log . Debugf ( "form %+v\n" , passwordForm )
err := bcrypt . CompareHashAndPassword ( [ ] byte ( user . Password ) , [ ] byte ( passwordForm . CurrentPassword ) )
if err != nil {
log . Error ( "Password Incorrect." )
return & user , http . StatusInternalServerError , errors . New ( "User is not updated. Password Incorrect." )
} else {
newPassword , err := bcrypt . GenerateFromPassword ( [ ] byte ( passwordForm . Password ) , 10 )
if err != nil {
return & user , http . StatusInternalServerError , errors . New ( "User is not updated. Password not Generated." )
} else {
passwordForm . Password = string ( newPassword )
modelHelper . AssignValue ( & user , & passwordForm )
}
}
default :
2017-05-07 00:10:40 +02:00
var form formStruct . UserForm
2017-05-06 22:27:21 +02:00
modelHelper . BindValueForm ( & form , r )
2017-05-06 21:21:39 +02:00
log . Debugf ( "form %+v\n" , form )
modelHelper . AssignValue ( & user , & form )
}
status , err := UpdateUserCore ( & user )
if err != nil {
return & user , status , err
}
status , err = SetCookie ( w , user . Token )
return & user , status , err
}
// DeleteUser deletes a user.
func DeleteUser ( w http . ResponseWriter , id string ) ( int , error ) {
var user model . User
if db . ORM . First ( & user , id ) . RecordNotFound ( ) {
return http . StatusNotFound , errors . New ( "User is not found." )
}
if db . ORM . Delete ( & user ) . Error != nil {
return http . StatusInternalServerError , errors . New ( "User is not deleted." )
}
status , err := ClearCookie ( w )
return status , err
}
// AddRoleToUser adds a role to a user.
func AddRoleToUser ( r * http . Request ) ( int , error ) {
2017-05-07 00:10:40 +02:00
var form formStruct . UserRoleForm
2017-05-06 21:21:39 +02:00
var user model . User
var role model . Role
var roles [ ] model . Role
modelHelper . BindValueForm ( & form , r )
if db . ORM . First ( & user , form . UserId ) . RecordNotFound ( ) {
return http . StatusNotFound , errors . New ( "User is not found." )
}
if db . ORM . First ( & role , form . RoleId ) . RecordNotFound ( ) {
return http . StatusNotFound , errors . New ( "Role is not found." )
}
log . Debugf ( "user email : %s" , user . Email )
log . Debugf ( "Role name : %s" , role . Name )
db . ORM . Model ( & user ) . Association ( "Roles" ) . Append ( role )
db . ORM . Model ( & user ) . Association ( "Roles" ) . Find ( & roles )
if db . ORM . Save ( & user ) . Error != nil {
return http . StatusInternalServerError , errors . New ( "Role not appended to user." )
}
return http . StatusOK , nil
}
// RemoveRoleFromUser removes a role from a user.
func RemoveRoleFromUser ( w http . ResponseWriter , r * http . Request , userId string , roleId string ) ( int , error ) {
var user model . User
var role model . Role
if db . ORM . First ( & user , userId ) . RecordNotFound ( ) {
return http . StatusNotFound , errors . New ( "User is not found." )
}
if db . ORM . First ( & role , roleId ) . RecordNotFound ( ) {
return http . StatusNotFound , errors . New ( "Role is not found." )
}
log . Debugf ( "user : %v\n" , user )
log . Debugf ( "role : %v\n" , role )
if db . ORM . Model ( & user ) . Association ( "Roles" ) . Delete ( role ) . Error != nil {
return http . StatusInternalServerError , errors . New ( "Role is not deleted from user." )
}
return http . StatusOK , nil
}
// RetrieveCurrentUser retrieves a current user.
func RetrieveCurrentUser ( r * http . Request ) ( model . User , int , error ) {
user , err := CurrentUser ( r )
if err != nil {
return user , http . StatusInternalServerError , err
}
return user , http . StatusOK , nil
}
// RetrieveUserByEmail retrieves a user by an email
func RetrieveUserByEmail ( email string ) ( * model . PublicUser , string , int , error ) {
var user model . User
if db . ORM . Unscoped ( ) . Select ( config . UserPublicFields ) . Where ( "email like ?" , "%" + email + "%" ) . First ( & user ) . RecordNotFound ( ) {
return & model . PublicUser { User : & user } , email , http . StatusNotFound , errors . New ( "User is not found." )
}
return & model . PublicUser { User : & user } , email , http . StatusOK , nil
}
// RetrieveUsersByEmail retrieves users by an email
func RetrieveUsersByEmail ( email string ) [ ] * model . PublicUser {
var users [ ] * model . User
var userArr [ ] * model . PublicUser
db . ORM . Select ( config . UserPublicFields ) . Where ( "email like ?" , "%" + email + "%" ) . Find ( & users )
for _ , user := range users {
userArr = append ( userArr , & model . PublicUser { User : user } )
}
return userArr
}
// RetrieveUserByUsername retrieves a user by username.
func RetrieveUserByUsername ( username string ) ( * model . PublicUser , string , int , error ) {
var user model . User
if db . ORM . Unscoped ( ) . Select ( config . UserPublicFields ) . Where ( "username like ?" , "%" + username + "%" ) . First ( & user ) . RecordNotFound ( ) {
return & model . PublicUser { User : & user } , username , http . StatusNotFound , errors . New ( "User is not found." )
}
return & model . PublicUser { User : & user } , username , http . StatusOK , nil
}
// RetrieveUserForAdmin retrieves a user for an administrator.
func RetrieveUserForAdmin ( id string ) ( model . User , int , error ) {
var user model . User
if db . ORM . First ( & user , id ) . RecordNotFound ( ) {
return user , http . StatusNotFound , errors . New ( "User is not found." )
}
db . ORM . Model ( & user ) . Association ( "Languages" ) . Find ( & user . Languages )
db . ORM . Model ( & user ) . Association ( "Roles" ) . Find ( & user . Roles )
return user , http . StatusOK , nil
}
// RetrieveUsersForAdmin retrieves users for an administrator.
func RetrieveUsersForAdmin ( ) [ ] model . User {
var users [ ] model . User
var userArr [ ] model . User
db . ORM . Find ( & users )
for _ , user := range users {
db . ORM . Model ( & user ) . Association ( "Languages" ) . Find ( & user . Languages )
db . ORM . Model ( & user ) . Association ( "Roles" ) . Find ( & user . Roles )
userArr = append ( userArr , user )
}
return userArr
}
// ActivateUser toggle activation of a user.
func ActivateUser ( r * http . Request , id string ) ( model . User , int , error ) {
var user model . User
2017-05-07 00:10:40 +02:00
var form formStruct . ActivateForm
2017-05-06 21:21:39 +02:00
modelHelper . BindValueForm ( & form , r )
if db . ORM . First ( & user , id ) . RecordNotFound ( ) {
return user , http . StatusNotFound , errors . New ( "User is not found." )
}
user . Activation = form . Activation
if db . ORM . Save ( & user ) . Error != nil {
return user , http . StatusInternalServerError , errors . New ( "User not activated." )
}
return user , http . StatusOK , nil
}
// CreateUserAuthentication creates user authentication.
func CreateUserAuthentication ( w http . ResponseWriter , r * http . Request ) ( int , error ) {
2017-05-07 00:10:40 +02:00
var form formStruct . LoginForm
2017-05-06 21:21:39 +02:00
modelHelper . BindValueForm ( & form , r )
2017-05-08 00:21:31 +02:00
username := form . Username
2017-05-06 21:21:39 +02:00
pass := form . Password
2017-05-08 00:21:31 +02:00
status , err := SetCookieHandler ( w , username , pass )
2017-05-06 21:21:39 +02:00
return status , err
}