diff --git a/config/env.go b/config/env.go
index 3999a39d..3d1c0504 100644
--- a/config/env.go
+++ b/config/env.go
@@ -5,4 +5,5 @@ const (
// DEVELOPMENT | TEST | PRODUCTION
Environment = "DEVELOPMENT"
// Environment = "PRODUCTION"
+ WebAddress = "nyaa.pantsu.cat"
)
diff --git a/router/userHandler.go b/router/userHandler.go
new file mode 100644
index 00000000..af4307f9
--- /dev/null
+++ b/router/userHandler.go
@@ -0,0 +1,47 @@
+package router
+
+import(
+ "github.com/gorilla/mux"
+ "net/http"
+ "html"
+ "strconv"
+ "github.com/ewhal/nyaa/model"
+ "github.com/ewhal/nyaa/service/user"
+)
+
+// Getting View User Registration
+
+func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
+// Getting View User Login
+func UserLoginFormHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
+// Getting User Profile
+func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
+// Getting View User Profile Update
+func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
+// Post Registration controller
+func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
+// Post Login controller
+func UserLoginPostHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
+// Post Profule Update controller
+func UserProfilePostHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
diff --git a/service/user/cookieHelper.go b/service/user/cookieHelper.go
new file mode 100644
index 00000000..5db7eaf1
--- /dev/null
+++ b/service/user/cookieHelper.go
@@ -0,0 +1,157 @@
+package userService
+
+import (
+ "errors"
+ "net/http"
+
+ "github.com/gorilla/securecookie"
+ "golang.org/x/crypto/bcrypt"
+
+ "github.com/ewhal/nyaa/config"
+ "github.com/ewhal/nyaa/db"
+ "github.com/ewhal/nyaa/model"
+ "github.com/ewhal/nyaa/util/log"
+ "github.com/ewhal/nyaa/util/validation"
+ "github.com//modelHelper"
+)
+
+var cookieHandler = securecookie.New(
+ securecookie.GenerateRandomKey(64),
+ securecookie.GenerateRandomKey(32))
+
+// // UserName get username from a cookie.
+// func UserName(c *gin.Context) (string, error) {
+// var userName string
+// request := c.Request
+// cookie, err := request.Cookie("session")
+// if err != nil {
+// return userName, err
+// }
+// cookieValue := make(map[string]string)
+// err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
+// if err != nil {
+// return userName, err
+// }
+// userName = cookieValue["name"]
+// return userName, nil
+// }
+
+func Token(w http.ResponseWriter, r *http.Request) (string, error) {
+ var token string
+ request := c.Request
+ cookie, err := request.Cookie("session")
+ if err != nil {
+ return token, err
+ }
+ cookieValue := make(map[string]string)
+ err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
+ if err != nil {
+ return token, err
+ }
+ token = cookieValue["token"]
+ if len(token) == 0 {
+ return token, errors.New("Token is empty.")
+ }
+ return token, nil
+}
+
+// SetCookie sets a cookie.
+func SetCookie(w http.ResponseWriter, token string) (int, error) {
+ value := map[string]string{
+ "token": token,
+ }
+ encoded, err := cookieHandler.Encode("session", value)
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ cookie := &http.Cookie{
+ Name: "session",
+ Value: encoded,
+ Path: "/",
+ }
+ http.SetCookie(w, cookie)
+ return http.StatusOK, nil
+}
+
+// ClearCookie clears a cookie.
+func ClearCookie(w http.ResponseWriter) (int, error) {
+ cookie := &http.Cookie{
+ Name: "session",
+ Value: "",
+ Path: "/",
+ MaxAge: -1,
+ }
+ http.SetCookie(w, cookie)
+ return http.StatusOK, nil
+}
+
+// SetCookieHandler sets a cookie with email and password.
+func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, error) {
+ if email != "" && pass != "" {
+ log.Debugf("User email : %s , password : %s", email, pass)
+ var user model.User
+ isValidEmail := validation.EmailValidation(email)
+ if isValidEmail {
+ log.Debug("User entered valid email.")
+ if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() {
+ return http.StatusNotFound, errors.New("User is not found.")
+ }
+ } else {
+ log.Debug("User entered username.")
+ if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() {
+ return http.StatusNotFound, errors.New("User is not found.")
+ }
+ }
+ err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass))
+ if err != nil {
+ return http.StatusUnauthorized, errors.New("Password incorrect.")
+ }
+ status, err := SetCookie(w, user.Token)
+ if err != nil {
+ return status, err
+ }
+ w.Header().Set("X-Auth-Token", user.Token)
+ return http.StatusOK, nil
+ } else {
+ return http.StatusNotFound, errors.New("User is not found.")
+ }
+}
+
+// RegisterHanderFromForm sets cookie from a RegistrationForm.
+func RegisterHanderFromForm(w http.ResponseWriter, registrationForm RegistrationForm) (int, error) {
+ email := registrationForm.Email
+ pass := registrationForm.Password
+ log.Debugf("RegisterHandler UserEmail : %s", email)
+ log.Debugf("RegisterHandler UserPassword : %s", pass)
+ return SetCookieHandler(w, email, pass)
+}
+
+// RegisterHandler sets a cookie when user registered.
+func RegisterHandler(w http.ResponseWriter, r *http.Request) (int, error) {
+ var registrationForm RegistrationForm
+ modelHelper.BindValueForm(®istrationForm, r)
+ return RegisterHanderFromForm(w, registrationForm)
+}
+
+// CurrentUser get a current user.
+func CurrentUser(r *http.Request) (model.User, error) {
+ var user model.User
+ var token string
+ var err error
+ token = r.Header.Get("X-Auth-Token")
+ if len(token) > 0 {
+ log.Debug("header token exist.")
+ } else {
+ token, err = Token(c)
+ log.Debug("header token not exist.")
+ if err != nil {
+ return user, err
+ }
+ }
+ if db.ORM.Select(config.UserPublicFields+", email").Where("token = ?", token).First(&user).RecordNotFound() {
+ return user, 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, nil
+}
diff --git a/service/user/formValidator.go b/service/user/formValidator.go
new file mode 100644
index 00000000..1ca06130
--- /dev/null
+++ b/service/user/formValidator.go
@@ -0,0 +1,69 @@
+package userService
+
+import (
+ "regexp"
+ "github.com/ewhal/nyaa/util/log"
+)
+
+const EMAIL_REGEX = `(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`
+
+func EmailValidation(email string) bool {
+ exp, err := regexp.Compile(EMAIL_REGEX)
+ if regexpCompiled := log.CheckError(err); regexpCompiled {
+ if exp.MatchString(email) {
+ return true
+ }
+ }
+ return false
+}
+
+// RegistrationForm is used when creating a user.
+type RegistrationForm struct {
+ Username string `form:"registrationUsername" binding:"required"`
+ Email string `form:"registrationEmail" binding:"required"`
+ Password string `form:"registrationPassword" binding:"required"`
+}
+
+// RegistrationForm is used when creating a user authentication.
+type LoginForm struct {
+ Email string `form:"loginEmail" binding:"required"`
+ Password string `form:"loginPassword" binding:"required"`
+}
+
+// UserForm is used when updating a user.
+type UserForm struct {
+ Email string `form:"email" binding:"required"`
+}
+
+// PasswordForm is used when updating a user password.
+type PasswordForm struct {
+ CurrentPassword string `form:"currentPassword" binding:"required"`
+ Password string `form:"newPassword" binding:"required"`
+}
+
+// SendPasswordResetForm is used when sending a password reset token.
+type SendPasswordResetForm struct {
+ Email string `form:"email" binding:"required"`
+}
+
+// PasswordResetForm is used when reseting a password.
+type PasswordResetForm struct {
+ PasswordResetToken string `form:"token" binding:"required"`
+ Password string `form:"newPassword" binding:"required"`
+}
+
+// VerifyEmailForm is used when verifying an email.
+type VerifyEmailForm struct {
+ ActivationToken string `form:"token" binding:"required"`
+}
+
+// ActivateForm is used when activating user.
+type ActivateForm struct {
+ Activation bool `form:"activation" binding:"required"`
+}
+
+// UserRoleForm is used when adding or removing a role from a user.
+type UserRoleForm struct {
+ UserId int `form:"userId" binding:"required"`
+ RoleId int `form:"roleId" binding:"required"`
+}
diff --git a/service/user/locale/en-us.all.json b/service/user/locale/en-us.all.json
new file mode 100644
index 00000000..28eaf0b1
--- /dev/null
+++ b/service/user/locale/en-us.all.json
@@ -0,0 +1,22 @@
+[
+ {
+ "id": "link",
+ "translation": "link"
+ },
+ {
+ "id": "verify_email_title",
+ "translation": "Verify your email address for Goyangi."
+ },
+ {
+ "id": "verify_email_content",
+ "translation": "Please click below link to verify your email."
+ },
+ {
+ "id": "reset_password_title",
+ "translation": "Reset your password for goyangi."
+ },
+ {
+ "id": "reset_password_content",
+ "translation": "Please click below link to reset your password."
+ }
+]
diff --git a/service/user/permission/permission.go b/service/user/permission/permission.go
new file mode 100644
index 00000000..5542b2a1
--- /dev/null
+++ b/service/user/permission/permission.go
@@ -0,0 +1,42 @@
+package userPermission
+
+import (
+ "errors"
+ "net/http"
+
+ "github.com/ewhal/nyaa/model"
+ "github.com/ewhal/nyaa/service/user"
+ "github.com/ewhal/nyaa/util/log"
+ "github.com/gin-gonic/gin"
+)
+
+// HasAdmin checks that user has an admin permission.
+func HasAdmin(user *model.User) bool {
+ name := "admin"
+ for _, role := range user.Roles {
+ log.Debugf("HasAdmin role.Name : %s", role.Name)
+ if role.Name == name {
+ return true
+ }
+ }
+ return false
+}
+
+// CurrentOrAdmin check that user has admin permission or user is the current user.
+func CurrentOrAdmin(user *model.User, userId uint) bool {
+ log.Debugf("user.Id == userId %d %d %s", user.Id, userId, user.Id == userId)
+ return (HasAdmin(user) || user.Id == userId)
+}
+
+// CurrentUserIdentical check that userId is same as current user's Id.
+func CurrentUserIdentical(userId uint) (bool, error) {
+ currentUser, err := userService.CurrentUser(c)
+ if err != nil {
+ return false, errors.New("Auth failed.")
+ }
+ if currentUser.Id != userId {
+ return false, errors.New("User is not identical.")
+ }
+
+ return true, nil
+}
\ No newline at end of file
diff --git a/service/user/user.go b/service/user/user.go
new file mode 100644
index 00000000..ff07e6cd
--- /dev/null
+++ b/service/user/user.go
@@ -0,0 +1,341 @@
+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"
+ "github.com/ewhal/nyaa/util/crypto"
+ "github.com/ewhal/nyaa/util/log"
+ "github.com/ewhal/nyaa/util/modelHelper"
+ "github.com/ewhal/nyaa/util/timeHelper"
+)
+
+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
+}
+
+// CreateUserFromForm creates a user from a registration form.
+func CreateUserFromForm(registrationForm RegistrationForm) (model.User, error) {
+ var user model.User
+ log.Debugf("registrationForm %+v\n", registrationForm)
+ modelHelper.AssignValue(&user, ®istrationForm)
+ 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
+ var registrationForm RegistrationForm
+ var status int
+ var err error
+
+ modelHelper.BindValueForm(®istrationForm, r)
+
+ 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)
+ status, err = RegisterHandler(w)
+ 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 currentPage int
+
+ currentPage = 1
+
+ var likings []model.User
+ var likingCount int
+ db.ORM.Table("users_followers").Where("users_followers.user_id=?", user.Id).Count(&likingCount)
+ if err = db.ORM.Limit(config.LikingPerPage).Order(config.LikingOrder).Offset(offset).Select(config.UserPublicFields).
+ 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 likingList model.LikingList
+
+ var liked []model.User
+ var likedCount int
+ db.ORM.Table("users_followers").Where("users_followers.follower_id=?", user.Id).Count(&likedCount)
+ if err = db.ORM.Limit(config.LikedPerPage).Order(config.LikedOrder).Offset(offset).Select(config.UserPublicFields).
+ 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":
+ var passwordForm PasswordForm
+ 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:
+ var form UserForm
+ modelHelper.BindValueForm(&form, binding.Form)
+ 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) {
+ var form UserRoleForm
+ 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) {
+ id := c.Params.ByName("id")
+ var user model.User
+ var form ActivateForm
+ 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) {
+ var form LoginForm
+ modelHelper.BindValueForm(&form, r)
+ email := form.Email
+ pass := form.Password
+ status, err := SetCookieHandler(w, email, pass)
+ return status, err
+}
diff --git a/service/user/userHelper.go b/service/user/userHelper.go
new file mode 100644
index 00000000..e1d48c31
--- /dev/null
+++ b/service/user/userHelper.go
@@ -0,0 +1,56 @@
+package userService
+
+import (
+ "github.com/ewha/nyaa/db"
+ "github.com/ewha/nyaa/model"
+ // "github.com/gin-gonic/gin"
+ "errors"
+ "net/http"
+
+ "github.com/ewha/nyaa/util/log"
+ "github.com/ewha/nyaa/util/retrieveHelper"
+ // "github.com/dorajistyle/goyangi/util/crypto"
+)
+
+// FindUserByUserName creates a user.
+func FindUserByUserName(appID int64, userName string) (model.User, int, error) {
+ var user model.User
+ var err error
+ // token := c.Request.Header.Get("X-Auth-Token")
+ if db.ORM.Where("app_id=? and name=?", appID, userName).First(&user).RecordNotFound() {
+ return user, http.StatusUnauthorized, err
+ }
+ return user, http.StatusOK, nil
+}
+
+// FindOrCreateUser creates a user.
+func FindOrCreateUser(appID int64, userName string) (model.User, int, error) {
+ var user model.User
+ var err error
+
+ // if len(token) > 0 {
+ // log.Debug("header token exist.")
+ // } else {
+ // token, err = Token(c)
+ // log.Debug("header token not exist.")
+ // if err != nil {
+ // return user, http.StatusUnauthorized, err
+ // }
+ // }
+ log.Debugf("userName : %s\n", userName)
+ // log.Debugf("Error : %s\n", err.Error())
+ if db.ORM.Where("app_id=? and name=?", appID, userName).First(&user).RecordNotFound() {
+ var user model.User
+ // return user, http.StatusBadRequest, err
+ user.Name = userName
+ // user.Token = token
+ user.AppID = appID
+ log.Debugf("user %+v\n", user)
+ if db.ORM.Create(&user).Error != nil {
+ return user, http.StatusBadRequest, errors.New("User is not created.")
+ }
+ log.Debugf("retrived User %v\n", user)
+ return user, http.StatusOK, nil
+ }
+ return user, http.StatusBadRequest, nil
+}
diff --git a/service/user/verification.go b/service/user/verification.go
new file mode 100644
index 00000000..7d76717a
--- /dev/null
+++ b/service/user/verification.go
@@ -0,0 +1,92 @@
+package userService
+
+import (
+ "errors"
+ "net/http"
+ "time"
+
+ "github.com/ewhal/nyaa/config"
+ "github.com/ewhal/nyaa/db"
+ "github.com/ewhal/nyaa/model"
+ "github.com/ewhal/nyaa/util/modelHandler"
+ "github.com/ewhal/nyaa/util/crypto"
+ "github.com/ewhal/nyaa/util/email"
+ "github.com/ewhal/nyaa/util/log"
+ "github.com/ewhal/nyaa/util/timeHelper"
+
+ "github.com/nicksnyder/go-i18n/i18n"
+)
+
+// SendEmailVerfication sends an email verification token via email.
+func SendEmailVerfication(to string, token string, locale string) error {
+ T, _ := i18n.Tfunc(locale)
+ err := email.SendEmailFromAdmin(to,
+ T("verify_email_title"),
+ T("link")+" : "+config.WebAddress+"/verify/email/"+token,
+ T("verify_email_content")+"
"+config.WebAddress+"/verify/email/"+token+"")
+ return err
+}
+
+// SendVerificationToUser sends an email verification token to user.
+func SendVerificationToUser(user model.User) (int, error) {
+ var status int
+ var err error
+ user.ActivateUntil = timeHelper.TwentyFourHoursLater()
+ user.ActivationToken, err = crypto.GenerateRandomToken32()
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ user.Activation = false
+ log.Debugf("generated token : %s", user.ActivationToken)
+ status, err = UpdateUserCore(&user)
+ if err != nil {
+ return status, err
+ }
+ err = SendEmailVerfication(user.Email, user.ActivationToken, "en-us")
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ return http.StatusOK, err
+}
+
+// SendVerification sends an email verification token.
+func SendVerification(r *http.Request) (int, error) {
+
+ var user model.User
+ currentUser, err := CurrentUser(r)
+ if err != nil {
+ return http.StatusUnauthorized, errors.New("Unauthorized.")
+ }
+ if db.ORM.First(&user, currentUser.Id).RecordNotFound() {
+ return http.StatusNotFound, errors.New("User is not found.")
+ }
+ status, err := SendVerificationToUser(user)
+ return status, err
+}
+
+// EmailVerification verifies an email of user.
+func EmailVerification(w http.ResponseWriter, r *http.Request) (int, error) {
+ var user model.User
+ var verifyEmailForm VerifyEmailForm
+ modelHandler.BindValueForm(&verifyEmailForm, r)
+ log.Debugf("verifyEmailForm.ActivationToken : %s", verifyEmailForm.ActivationToken)
+ if db.ORM.Where(&model.User{ActivationToken: verifyEmailForm.ActivationToken}).First(&user).RecordNotFound() {
+ return http.StatusNotFound, errors.New("User is not found.")
+ }
+ isExpired := timeHelper.IsExpired(user.ActivateUntil)
+ log.Debugf("passwordResetUntil : %s", user.ActivateUntil.UTC())
+ log.Debugf("expired : %t", isExpired)
+ if isExpired {
+ return http.StatusForbidden, errors.New("token not valid.")
+ }
+ user.ActivationToken = ""
+ user.ActivateUntil = time.Now()
+ user.ActivatedAt = time.Now()
+ user.Activation = true
+ status, err := UpdateUserCore(&user)
+ if err != nil {
+ return status, err
+ }
+ status, err = SetCookie(<, user.Token)
+ return status, err
+}
diff --git a/util/crypto/crypto.go b/util/crypto/crypto.go
new file mode 100644
index 00000000..cbb912a3
--- /dev/null
+++ b/util/crypto/crypto.go
@@ -0,0 +1,29 @@
+package crypto
+
+import (
+ "crypto/md5"
+ "fmt"
+ "strings"
+)
+
+func GenerateMD5Hash(email string) string {
+ email = strings.ToLower(strings.TrimSpace(email))
+ hash := md5.New()
+ hash.Write([]byte(email))
+ return fmt.Sprintf("%x", hash.Sum(nil))
+}
+func GenerateRandomToken16() (string, error) {
+ return GenerateRandomToken(16)
+}
+
+func GenerateRandomToken32() (string, error) {
+ return GenerateRandomToken(32)
+}
+
+func GenerateRandomToken(n int) (string, error) {
+ token := make([]byte, n)
+ _, err := rand.Read(token)
+ // %x base 16, lower-case, two characters per byte
+ return fmt.Sprintf("%x", token), err
+
+}
diff --git a/util/timeHelper/timeHelper.go b/util/timeHelper/timeHelper.go
new file mode 100644
index 00000000..d7634c8b
--- /dev/null
+++ b/util/timeHelper/timeHelper.go
@@ -0,0 +1,53 @@
+package timeHelper
+
+import (
+ "time"
+
+ "github.com/ewhal/nyaa/util/log"
+)
+
+func FewDaysLater(day int) time.Time {
+ return FewDurationLater(time.Duration(day) * 24 * time.Hour)
+}
+
+func TwentyFourHoursLater() time.Time {
+ return FewDurationLater(time.Duration(24) * time.Hour)
+}
+
+func SixHoursLater() time.Time {
+ return FewDurationLater(time.Duration(6) * time.Hour)
+}
+
+func InTimeSpan(start, end, check time.Time) bool {
+ log.Debugf("check after before: %s %t %t\n", check, check.After(start), check.Before(end))
+ return check.After(start) && check.Before(end)
+}
+
+func InTimeSpanNow(start, end time.Time) bool {
+ now := time.Now()
+ return InTimeSpan(start, end, now)
+}
+
+func FewDurationLater(duration time.Duration) time.Time {
+ // When Save time should considering UTC
+ // baseTime := time.Now()
+ // log.Debugf("basetime : %s", baseTime)
+ fewDurationLater := time.Now().Add(duration)
+ log.Debugf("time : %s", fewDurationLater)
+ return fewDurationLater
+}
+
+func FewDurationLaterMillisecond(duration time.Duration) int64 {
+ return FewDurationLater(duration).UnixNano() / int64(time.Millisecond)
+}
+
+func IsExpired(expirationTime time.Time) bool {
+ // baseTime := time.Now()
+ // log.Debugf("basetime : %s", baseTime)
+ log.Debugf("expirationTime : %s", expirationTime)
+ // elapsed := time.Since(expirationTime)
+ // log.Debugf("elapsed : %s", elapsed)
+ after := time.Now().After(expirationTime)
+ log.Debugf("after : %t", after)
+ return after
+}