2017-05-06 21:21:39 +02:00
package userService
import (
"errors"
2017-05-09 01:56:57 +02:00
"fmt"
2017-05-06 21:21:39 +02:00
"net/http"
"strconv"
2017-05-08 19:26:29 +02:00
"time"
2017-05-06 21:21:39 +02:00
"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-09 17:47:06 +02:00
"github.com/ewhal/nyaa/service/user/permission"
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-09 01:56:57 +02:00
"golang.org/x/crypto/bcrypt"
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-08 22:12:57 +02:00
2017-05-07 21:14:32 +02:00
func CheckEmail ( email string ) bool {
2017-05-08 22:12:57 +02:00
if len ( email ) == 0 {
2017-05-09 17:06:21 +02:00
return false
2017-05-08 22:12:57 +02:00
}
2017-05-07 21:14:32 +02:00
var count int
2017-05-08 22:12:57 +02:00
db . ORM . Model ( model . User { } ) . Where ( "email = ?" , email ) . Count ( & count )
2017-05-09 17:06:21 +02:00
if count != 0 {
return true // error: duplicate
2017-05-07 21:14:32 +02:00
}
2017-05-09 17:06:21 +02:00
return false
2017-05-07 21:14:32 +02:00
}
2017-05-08 22:12:57 +02:00
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 )
2017-05-09 17:20:19 +02:00
if user . Email == "" {
user . Md5 = ""
} else {
user . Md5 = crypto . GenerateMD5Hash ( user . Email )
}
2017-05-06 21:21:39 +02:00
token , err := crypto . GenerateRandomToken32 ( )
if err != nil {
return user , errors . New ( "Token not generated." )
}
2017-05-09 17:20:19 +02:00
2017-05-06 21:21:39 +02:00
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." )
}
2017-05-09 17:20:19 +02:00
user . CreatedAt = time . Now ( )
2017-05-06 21:21:39 +02:00
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
2017-05-09 01:56:57 +02:00
2017-05-06 21:21:39 +02:00
modelHelper . BindValueForm ( & registrationForm , r )
2017-05-07 21:14:32 +02:00
usernameCandidate := SuggestUsername ( registrationForm . Username )
2017-05-09 01:56:57 +02:00
if usernameCandidate != registrationForm . Username {
2017-05-07 21:14:32 +02:00
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 ) {
2017-05-08 20:21:11 +02:00
var user model . User
2017-05-06 21:21:39 +02:00
var currentUserId uint
var isAuthor bool
// var publicUser *model.PublicUser
// publicUser.User = &user
2017-05-08 22:33:40 +02:00
if db . ORM . First ( & user , id ) . RecordNotFound ( ) {
2017-05-08 20:21:11 +02:00
return nil , isAuthor , currentUserId , http . StatusNotFound , errors . New ( "User is not found." )
2017-05-06 21:21:39 +02:00
}
currentUser , err := CurrentUser ( r )
if err == nil {
currentUserId = currentUser . Id
isAuthor = currentUser . Id == user . Id
}
2017-05-08 20:21:11 +02:00
return & model . PublicUser { User : & user } , isAuthor , currentUserId , http . StatusOK , nil
2017-05-06 21:21:39 +02:00
}
// RetrieveUsers retrieves users.
func RetrieveUsers ( ) [ ] * model . PublicUser {
var users [ ] * model . User
var userArr [ ] * model . PublicUser
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 ) {
2017-05-09 17:20:19 +02:00
if user . Email == "" {
user . Md5 = ""
} else {
user . Md5 = crypto . GenerateMD5Hash ( user . Email )
}
2017-05-06 21:21:39 +02:00
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." )
}
2017-05-09 17:20:19 +02:00
2017-05-08 19:26:29 +02:00
user . UpdatedAt = time . Now ( )
2017-05-06 21:21:39 +02:00
return http . StatusOK , nil
}
// UpdateUser updates a user.
2017-05-09 17:47:06 +02:00
func UpdateUser ( w http . ResponseWriter , form * formStruct . UserForm , currentUser * model . User , id string ) ( model . User , int , error ) {
2017-05-06 21:21:39 +02:00
var user model . User
if db . ORM . First ( & user , id ) . RecordNotFound ( ) {
2017-05-09 17:47:06 +02:00
return user , http . StatusNotFound , errors . New ( "User is not found." )
}
2017-05-09 23:21:15 +02:00
if form . Password != "" {
2017-05-09 17:47:06 +02:00
err := bcrypt . CompareHashAndPassword ( [ ] byte ( user . Password ) , [ ] byte ( form . CurrentPassword ) )
if err != nil && ! userPermission . HasAdmin ( currentUser ) {
2017-05-06 21:21:39 +02:00
log . Error ( "Password Incorrect." )
2017-05-09 17:47:06 +02:00
return user , http . StatusInternalServerError , errors . New ( "User is not updated. Password Incorrect." )
2017-05-06 21:21:39 +02:00
} else {
2017-05-09 17:47:06 +02:00
newPassword , err := bcrypt . GenerateFromPassword ( [ ] byte ( form . Password ) , 10 )
2017-05-06 21:21:39 +02:00
if err != nil {
2017-05-09 17:47:06 +02:00
return user , http . StatusInternalServerError , errors . New ( "User is not updated. Password not Generated." )
2017-05-06 21:21:39 +02:00
} else {
2017-05-09 17:47:06 +02:00
form . Password = string ( newPassword )
2017-05-06 21:21:39 +02:00
}
}
2017-05-09 17:47:06 +02:00
} else { // Then no change of password
form . Password = user . Password
2017-05-06 21:21:39 +02:00
}
2017-05-09 17:47:06 +02:00
if ! userPermission . HasAdmin ( currentUser ) { // We don't want users to be able to modify some fields
form . Status = user . Status
form . Username = user . Username
}
2017-05-09 23:21:15 +02:00
log . Debugf ( "form %+v\n" , form )
modelHelper . AssignValue ( & user , form )
2017-05-06 21:21:39 +02:00
status , err := UpdateUserCore ( & user )
if err != nil {
2017-05-09 17:47:06 +02:00
return user , status , err
}
2017-05-09 23:21:15 +02:00
if userPermission . CurrentUserIdentical ( currentUser , user . Id ) {
2017-05-09 17:47:06 +02:00
status , err = SetCookie ( w , user . Token )
2017-05-06 21:21:39 +02:00
}
2017-05-09 17:47:06 +02:00
return user , status , err
2017-05-06 21:21:39 +02:00
}
// DeleteUser deletes a user.
2017-05-09 17:47:06 +02:00
func DeleteUser ( w http . ResponseWriter , currentUser * model . User , id string ) ( int , error ) {
2017-05-06 21:21:39 +02:00
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." )
}
2017-05-09 23:21:15 +02:00
if userPermission . CurrentUserIdentical ( currentUser , user . Id ) {
return ClearCookie ( w )
2017-05-09 17:47:06 +02:00
}
return http . StatusOK , nil
2017-05-06 21:21:39 +02:00
}
// 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
2017-05-08 22:33:40 +02:00
if db . ORM . Unscoped ( ) . Where ( "email = ?" , email ) . First ( & user ) . RecordNotFound ( ) {
2017-05-06 21:21:39 +02:00
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
2017-05-08 22:33:40 +02:00
db . ORM . Where ( "email = ?" , email ) . Find ( & users )
2017-05-06 21:21:39 +02:00
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
2017-05-08 22:33:40 +02:00
if db . ORM . Where ( "username = ?" , username ) . First ( & user ) . RecordNotFound ( ) {
2017-05-06 21:21:39 +02:00
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
2017-05-09 07:21:14 +02:00
if db . ORM . Preload ( "Torrents" ) . First ( & user , id ) . RecordNotFound ( ) {
2017-05-06 21:21:39 +02:00
return user , http . StatusNotFound , errors . New ( "User is not found." )
}
2017-05-10 03:15:29 +02:00
var liked , likings [ ] model . User
db . ORM . Joins ( "JOIN user_follows on user_follows.user_id=?" , user . Id ) . Where ( "users.user_id = user_follows.following" ) . Group ( "users.user_id" ) . Find ( & likings )
db . ORM . Joins ( "JOIN user_follows on user_follows.following=?" , user . Id ) . Where ( "users.user_id = user_follows.user_id" ) . Group ( "users.user_id" ) . Find ( & liked )
user . Likings = likings
user . Liked = liked
2017-05-06 21:21:39 +02:00
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 {
2017-05-08 19:26:29 +02:00
db . ORM . Model ( & user )
2017-05-10 03:15:29 +02:00
db . ORM . Model ( & user ) . Related ( "Torrents" ) . Related ( "Likings" ) . Related ( "Liked" ) . Find ( & model . Torrents { } )
2017-05-06 21:21:39 +02:00
userArr = append ( userArr , user )
}
return userArr
}
// 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
}
2017-05-10 03:15:29 +02:00
func SetFollow ( user * model . User , follower * model . User ) {
if ( follower . Id > 0 && user . Id > 0 ) {
var userFollows = model . UserFollows { UserID : user . Id , FollowerID : follower . Id }
db . ORM . Create ( & userFollows )
}
}
func RemoveFollow ( user * model . User , follower * model . User ) {
if ( follower . Id > 0 && user . Id > 0 ) {
var userFollows = model . UserFollows { UserID : user . Id , FollowerID : follower . Id }
db . ORM . Delete ( & userFollows )
}
2017-05-10 04:03:25 +02:00
}
func DeleteComment ( id string ) {
var comment model . Comment
if db . ORM . First ( & comment , id ) . RecordNotFound ( ) {
return http . StatusNotFound , errors . New ( "Comment is not found." )
}
if db . ORM . Delete ( & comment ) . Error != nil {
return http . StatusInternalServerError , errors . New ( "Comment is not deleted." )
}
return http . StatusOK , nil
2017-05-10 03:15:29 +02:00
}