Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Merge remote-tracking branch 'origin/master' into compile-time-templates

Cette révision appartient à :
bakape 2017-05-07 00:26:46 +03:00
révision 7a49c3da54
23 fichiers modifiés avec 1045 ajouts et 52 suppressions

Voir le fichier

@ -8,6 +8,10 @@ install:
- go get github.com/jinzhu/gorm
- go get github.com/Sirupsen/logrus
- go get gopkg.in/natefinch/lumberjack.v2
- go get gopkg.in/gomail.v2
- go get github.com/gorilla/securecookie
- go get golang.org/x/crypto/bcrypt
- go get github.com/nicksnyder/go-i18n/i18n
- go build
deploy:
provider: releases

15
config/email.go Fichier normal
Voir le fichier

@ -0,0 +1,15 @@
package config
import "time"
const (
SendEmail = true
EmailFrom = "donotrespond@nyaa.pantsu.cat"
EmailTestTo = ""
EmailHost = "localhost"
EmailUsername = ""
EmailPassword = ""
EmailPort = 465
// EmailTimeout = 80 * time.Millisecond
EmailTimeout = 10 * time.Second
)

Voir le fichier

@ -5,4 +5,6 @@ const (
// DEVELOPMENT | TEST | PRODUCTION
Environment = "DEVELOPMENT"
// Environment = "PRODUCTION"
WebAddress = "nyaa.pantsu.cat"
AuthTokenExpirationDay = 1000
)

6
config/user.go Fichier normal
Voir le fichier

@ -0,0 +1,6 @@
package config
// Constants for public models.
const (
UserPublicFields = "id, username, md5, description, created_at, liking_count, liked_count"
)

7
model/language.go Fichier normal
Voir le fichier

@ -0,0 +1,7 @@
package model
// Language is a language model.
type Language struct {
Id uint `json:"id"`
Name string `json:"name"`
}

7
model/status.go Fichier normal
Voir le fichier

@ -0,0 +1,7 @@
package model
//user status e.g. verified, filtered, etc
type Status struct {
Id int `json:"id"`
Name string `json:"name",sql:"size:255"`
}

Voir le fichier

@ -36,13 +36,17 @@ type User struct {
LastLoginIp string `json:"lastLoginIp",sql:"size:100"`
CurrentLoginIp string `json:"currentLoginIp",sql:"size:100"`
Status Status `gorm:"many2one:users_status;"`
// Liking
LikingCount int `json:"likingCount"`
LikedCount int `json:"likedCount"`
Likings []User `gorm:"foreignkey:userId;associationforeignkey:follower_id;many2many:users_followers;"`
Liked []User `gorm:"foreignkey:follower_id;associationforeignkey:userId;many2many:users_followers;"`
LikingCount int `json:"likingCount"`
LikedCount int `json:"likedCount"`
Likings []User `gorm:"foreignkey:userId;associationforeignkey:follower_id;many2many:users_followers;"`
Liked []User `gorm:"foreignkey:follower_id;associationforeignkey:userId;many2many:users_followers;"`
Connections []Connection
Languages []Language `gorm:"many2many:user_languages;"` // Many To Many, user_languages is the join table
Roles []Role `gorm:"many2many:users_roles;"` // Many To Many, users_roles
Torrents []Torrents
}
@ -76,8 +80,9 @@ type PublicUser struct {
CurrentLoginIp omit `json:"currentLoginIp,omitempty",sql:"size:100"`
Connections omit `json:"connections,omitempty"`
Languages omit `json:"languages,omitempty"`
Roles omit `json:"roles,omitempty"`
Torrents omit `json:"articles,omitempty"`
Torrents omit `json:"articles,omitempty"` //should user torrents not be displayed?
}
// Connection is a connection model for oauth.

47
router/userHandler.go Fichier normal
Voir le fichier

@ -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) {
}

Voir le fichier

@ -43,7 +43,7 @@ func GetFeeds() []model.Feed {
func GetTorrentById(id string) (model.Torrents, error) {
var torrent model.Torrents
if db.ORM.Where("torrent_id = ?", id).Find(&torrent).RecordNotFound() {
if db.ORM.Where("torrent_id = ?", id).Preload("Sub_Categories").Find(&torrent).RecordNotFound() {
return torrent, errors.New("Article is not found.")
}
@ -124,4 +124,4 @@ func CreateWhereParams(conditions string, params ...string) WhereParams {
}
return whereParams
}
}

155
service/user/cookieHelper.go Fichier normal
Voir le fichier

@ -0,0 +1,155 @@
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/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(r *http.Request) (string, error) {
var token string
cookie, err := r.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 := 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(&registrationForm, 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(r)
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
}

69
service/user/formValidator.go Fichier normal
Voir le fichier

@ -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"`
}

Voir le fichier

@ -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."
}
]

Voir le fichier

@ -0,0 +1,40 @@
package userPermission
import (
"errors"
"net/http"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service/user"
"github.com/ewhal/nyaa/util/log"
)
// 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(r *http.Request, userId uint) (bool, error) {
currentUser, err := userService.CurrentUser(r)
if err != nil {
return false, errors.New("Auth failed.")
}
if currentUser.Id != userId {
return false, errors.New("User is not identical.")
}
return true, nil
}

336
service/user/user.go Fichier normal
Voir le fichier

@ -0,0 +1,336 @@
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, &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
var registrationForm RegistrationForm
var status int
var err error
modelHelper.BindValueForm(&registrationForm, 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, r)
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)
if err = db.ORM.Order("created_at desc").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 liked []model.User
var likedCount int
db.ORM.Table("users_followers").Where("users_followers.follower_id=?", user.Id).Count(&likedCount)
if err = db.ORM.Order("created_at desc").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, r)
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) {
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
}

36
service/user/userHelper.go Fichier normal
Voir le fichier

@ -0,0 +1,36 @@
package userService
import (
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"errors"
"net/http"
"github.com/ewhal/nyaa/util/log"
)
// FindUserByUserName creates a user.
func FindUserByUserName(userName string) (model.User, int, error) {
var user model.User
var err error
if db.ORM.Where("name=?", userName).First(&user).RecordNotFound() {
return user, http.StatusUnauthorized, err
}
return user, http.StatusOK, nil
}
// FindOrCreateUser creates a user.
func FindOrCreateUser(username string) (model.User, int, error) {
var user model.User
if db.ORM.Where("username=?", username).First(&user).RecordNotFound() {
var user model.User
user.Username = username
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
}

92
service/user/verification.go Fichier normal
Voir le fichier

@ -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/modelHelper"
"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")+"<br/><a href=\""+config.WebAddress+"/verify/email/"+token+"\" target=\"_blank\">"+config.WebAddress+"/verify/email/"+token+"</a>")
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
modelHelper.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(w, user.Token)
return status, err
}

Voir le fichier

@ -63,6 +63,7 @@ Single search optiion field of a <select>
{"torrent_name", "Name"},
{"date", "Date"},
{"downloads", "Downloads"},
{"filesize", "Size"},
}) %}
</select>
<select name="order" class="form-control input-sm">

Voir le fichier

@ -221,88 +221,89 @@ func StreamSearchAdvanced(qw422016 *qt422016.Writer, nav Navigation, search Sear
{"torrent_name", "Name"},
{"date", "Date"},
{"downloads", "Downloads"},
{"filesize", "Size"},
})
//line search.html:66
//line search.html:67
qw422016.N().S(`
</select>
<select name="order" class="form-control input-sm">
`)
//line search.html:69
//line search.html:70
streamsearchOptions(qw422016, search.Order, []searchField{
{"desc", "Descending"},
{"asc", "Ascending"},
})
//line search.html:72
//line search.html:73
qw422016.N().S(`
</select>
<select name="max" class="form-control input-sm">
`)
//line search.html:75
//line search.html:76
sel := strconv.Itoa(nav.MaxItemPerPage)
//line search.html:75
//line search.html:76
qw422016.N().S(`
`)
//line search.html:76
//line search.html:77
for _, m := range [...]int{5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 70, 100, 150, 200, 300} {
//line search.html:76
//line search.html:77
qw422016.N().S(`
`)
//line search.html:77
//line search.html:78
id := strconv.Itoa(m)
//line search.html:77
//line search.html:78
qw422016.N().S(`
`)
//line search.html:78
//line search.html:79
streamsearchOption(qw422016, sel, searchField{id, id})
//line search.html:78
//line search.html:79
qw422016.N().S(`
`)
//line search.html:79
//line search.html:80
}
//line search.html:79
//line search.html:80
qw422016.N().S(`
</select>
`)
//line search.html:81
//line search.html:82
}
//line search.html:81
//line search.html:82
func WriteSearchAdvanced(qq422016 qtio422016.Writer, nav Navigation, search SearchForm) {
//line search.html:81
//line search.html:82
qw422016 := qt422016.AcquireWriter(qq422016)
//line search.html:81
//line search.html:82
StreamSearchAdvanced(qw422016, nav, search)
//line search.html:81
//line search.html:82
qt422016.ReleaseWriter(qw422016)
//line search.html:81
//line search.html:82
}
//line search.html:81
//line search.html:82
func SearchAdvanced(nav Navigation, search SearchForm) string {
//line search.html:81
//line search.html:82
qb422016 := qt422016.AcquireByteBuffer()
//line search.html:81
//line search.html:82
WriteSearchAdvanced(qb422016, nav, search)
//line search.html:81
//line search.html:82
qs422016 := string(qb422016.B)
//line search.html:81
//line search.html:82
qt422016.ReleaseByteBuffer(qb422016)
//line search.html:81
//line search.html:82
return qs422016
//line search.html:81
//line search.html:82
}
//line search.html:83
//line search.html:84
func StreamSearchButton(qw422016 *qt422016.Writer, search SearchForm) {
//line search.html:83
//line search.html:84
qw422016.N().S(`
<div class="input-group">
<input name="q" class="form-control input-sm" placeholder="Search" type="text" value="`)
//line search.html:85
//line search.html:86
qw422016.E().S(search.Query)
//line search.html:85
//line search.html:86
qw422016.N().S(`">
<span class="input-group-btn">
<button type="submit" class="btn btn-sm btn-success">
@ -312,31 +313,31 @@ func StreamSearchButton(qw422016 *qt422016.Writer, search SearchForm) {
</span>
</div>
`)
//line search.html:93
//line search.html:94
}
//line search.html:93
//line search.html:94
func WriteSearchButton(qq422016 qtio422016.Writer, search SearchForm) {
//line search.html:93
//line search.html:94
qw422016 := qt422016.AcquireWriter(qq422016)
//line search.html:93
//line search.html:94
StreamSearchButton(qw422016, search)
//line search.html:93
//line search.html:94
qt422016.ReleaseWriter(qw422016)
//line search.html:93
//line search.html:94
}
//line search.html:93
//line search.html:94
func SearchButton(search SearchForm) string {
//line search.html:93
//line search.html:94
qb422016 := qt422016.AcquireByteBuffer()
//line search.html:93
//line search.html:94
WriteSearchButton(qb422016, search)
//line search.html:93
//line search.html:94
qs422016 := string(qb422016.B)
//line search.html:93
//line search.html:94
qt422016.ReleaseByteBuffer(qb422016)
//line search.html:93
//line search.html:94
return qs422016
//line search.html:93
//line search.html:94
}

Voir le fichier

@ -9,7 +9,10 @@
{{if eq .Status 3}}class="trusted"{{end}}
{{if eq .Status 4}}class="aplus"{{end}}>
<td>Name</td>
<td>{{.Name}}</td>
<td>
{{.Name}}
<img style="float:right" src="{{$.URL.Parse (printf "/img/torrents/%s.png" .Sub_Category.Id) }}">
</td>
</tr>
<tr>
<td>Hash</td>

30
util/crypto/crypto.go Fichier normal
Voir le fichier

@ -0,0 +1,30 @@
package crypto
import (
"crypto/md5"
"crypto/rand"
"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
}

59
util/email/email.go Fichier normal
Voir le fichier

@ -0,0 +1,59 @@
package email
import (
"path/filepath"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/util/log"
gomail "gopkg.in/gomail.v2"
)
type EmailError error
var (
mailer = InitGomail()
)
func InitGomail() *gomail.Dialer {
mailer := gomail.NewDialer(config.EmailHost, config.EmailPort, config.EmailUsername, config.EmailPassword)
return mailer
}
func SendEmailFromAdmin(to string, subject string, body string, bodyHTML string) error {
msg := gomail.NewMessage()
msg.SetHeader("From", config.EmailFrom)
msg.SetHeader("To", to, config.EmailTestTo)
msg.SetHeader("Subject", subject)
msg.SetBody("text/plain", body)
msg.AddAlternative("text/html", bodyHTML)
log.Debugf("to : %s", to)
log.Debugf("subject : %s", subject)
log.Debugf("body : %s", body)
log.Debugf("bodyHTML : %s", bodyHTML)
if config.SendEmail {
log.Debug("SendEmail performed.")
err := mailer.DialAndSend(msg);
return err
}
return nil
}
func SendTestEmail() error {
msg := gomail.NewMessage()
msg.SetHeader("From", config.EmailFrom)
msg.SetHeader("To", config.EmailTestTo)
msg.SetAddressHeader("Cc", config.EmailTestTo, "NyaaPantsu")
msg.SetHeader("Subject", "Hi(안녕하세요)?!")
msg.SetBody("text/plain", "Hi(안녕하세요)?!")
msg.AddAlternative("text/html", "<p><b>Nowplay(나우플레이)</b> means <i>Let's play</i>!!?</p>")
path, err := filepath.Abs("img/megumin.png")
if err != nil {
panic(err)
}
msg.Attach(path)
// SendEmail(msg)
err = mailer.DialAndSend(msg)
return err
}

Voir le fichier

@ -7,7 +7,10 @@ import (
func FormatFilesize(bytes int64) string {
var unit string
var value float64
if bytes > 1024*1024*1024 {
if bytes > 1024*1024*1024*1024 {
unit = "TiB"
value = float64(bytes) / (1024*1024*1024*1024)
} else if bytes > 1024*1024*1024 {
unit = "GiB"
value = float64(bytes) / (1024*1024*1024)
} else if bytes > 1024*1024 {

53
util/timeHelper/timeHelper.go Fichier normal
Voir le fichier

@ -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
}