This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
package models
2017-05-06 17:55:02 +02:00
import (
2017-05-21 20:20:40 +02:00
"encoding/json"
2017-05-22 00:22:42 +02:00
"fmt"
2017-05-24 09:11:13 +02:00
"time"
2017-05-20 12:45:27 +02:00
2017-07-02 18:00:12 +02:00
"github.com/NyaaPantsu/nyaa/utils/log"
"net/http"
2017-07-02 23:53:23 +02:00
"errors"
2017-05-20 12:45:27 +02:00
"github.com/NyaaPantsu/nyaa/config"
2017-07-02 18:00:12 +02:00
"github.com/NyaaPantsu/nyaa/utils/crypto"
2017-05-06 17:55:02 +02:00
)
2017-05-20 01:10:16 +02:00
const (
2017-05-26 12:12:52 +02:00
// UserStatusBanned : Int for User status banned
UserStatusBanned = - 1
// UserStatusMember : Int for User status member
UserStatusMember = 0
// UserStatusTrusted : Int for User status trusted
UserStatusTrusted = 1
// UserStatusModerator : Int for User status moderator
2017-05-20 01:10:16 +02:00
UserStatusModerator = 2
2017-06-07 03:14:57 +02:00
// UserStatusScraped : Int for User status scrapped
UserStatusScraped = 3
2017-05-20 01:10:16 +02:00
)
2017-05-26 12:12:52 +02:00
// User model
2017-05-06 17:55:02 +02:00
type User struct {
2017-05-20 01:10:16 +02:00
ID uint ` gorm:"column:user_id;primary_key" `
2017-06-14 08:42:15 +02:00
Username string ` gorm:"column:username;unique" `
2017-05-20 01:10:16 +02:00
Password string ` gorm:"column:password" `
2017-06-14 08:42:15 +02:00
Email string ` gorm:"column:email;unique" `
2017-05-20 01:10:16 +02:00
Status int ` gorm:"column:status" `
CreatedAt time . Time ` gorm:"column:created_at" `
UpdatedAt time . Time ` gorm:"column:updated_at" `
2017-05-26 12:12:52 +02:00
APIToken string ` gorm:"column:api_token" `
APITokenExpiry time . Time ` gorm:"column:api_token_expiry" `
2017-05-20 01:10:16 +02:00
Language string ` gorm:"column:language" `
2017-05-27 19:08:47 +02:00
Theme string ` gorm:"column:theme" `
2017-05-31 04:21:35 +02:00
Mascot string ` gorm:"column:mascot" `
2017-06-05 16:32:48 +02:00
MascotURL string ` gorm:"column:mascot_url" `
2017-05-21 18:13:28 +02:00
UserSettings string ` gorm:"column:settings" `
2017-05-09 12:14:12 +02:00
// TODO: move this to PublicUser
2017-05-27 00:45:18 +02:00
Followers [ ] User // Don't work `gorm:"foreignkey:user_id;associationforeignkey:follower_id;many2many:user_follows"`
Likings [ ] User // Don't work `gorm:"foreignkey:follower_id;associationforeignkey:user_id;many2many:user_follows"`
2017-05-09 12:14:12 +02:00
2017-05-24 09:11:13 +02:00
MD5 string ` json:"md5" gorm:"column:md5" ` // Hash of email address, used for Gravatar
Torrents [ ] Torrent ` gorm:"ForeignKey:UploaderID" `
2017-05-21 18:13:28 +02:00
Notifications [ ] Notification ` gorm:"ForeignKey:UserID" `
2017-05-20 20:53:05 +02:00
2017-05-24 09:11:13 +02:00
UnreadNotifications int ` gorm:"-" ` // We don't want to loop every notifications when accessing user unread notif
Settings UserSettings ` gorm:"-" ` // We don't want to load settings everytime, stock it as a string, parse it when needed
2017-05-10 10:27:17 +02:00
}
2017-05-26 12:12:52 +02:00
// UserJSON : User model conversion in JSON
2017-05-10 21:09:37 +02:00
type UserJSON struct {
ID uint ` json:"user_id" `
Username string ` json:"username" `
Status int ` json:"status" `
2017-06-13 08:01:57 +02:00
APIToken string ` json:"token" `
MD5 string ` json:"md5" `
2017-05-10 21:09:37 +02:00
CreatedAt string ` json:"created_at" `
LikingCount int ` json:"liking_count" `
LikedCount int ` json:"liked_count" `
}
This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
// UserFollows association table : different users following eachother
type UserFollows struct {
UserID uint ` gorm:"column:user_id" `
FollowerID uint ` gorm:"column:following" `
}
// UserUploadsOld model : Is it deprecated?
type UserUploadsOld struct {
Username string ` gorm:"column:username" `
TorrentID uint ` gorm:"column:torrent_id" `
}
// UserSettings : Struct for user settings, not a model
type UserSettings struct {
Settings map [ string ] bool ` json:"settings" `
}
/ *
* User Model
* /
2017-05-26 12:12:52 +02:00
// Size : Returns the total size of memory recursively allocated for this struct
2017-05-10 10:27:17 +02:00
func ( u User ) Size ( ) ( s int ) {
s += 4 + // ints
6 * 2 + // string pointers
4 * 3 + //time.Time
3 * 2 + // arrays
// string arrays
2017-05-27 19:08:47 +02:00
len ( u . Username ) + len ( u . Password ) + len ( u . Email ) + len ( u . APIToken ) + len ( u . MD5 ) + len ( u . Language ) + len ( u . Theme )
2017-05-10 10:27:17 +02:00
s *= 8
// Ignoring foreign key users. Fuck them.
return
2017-05-06 17:55:02 +02:00
}
2017-05-26 12:12:52 +02:00
// IsBanned : Return true if user is banned
func ( u * User ) IsBanned ( ) bool {
2017-05-20 01:10:16 +02:00
return u . Status == UserStatusBanned
}
2017-05-26 12:12:52 +02:00
// IsMember : Return true if user is member
func ( u * User ) IsMember ( ) bool {
2017-05-20 01:10:16 +02:00
return u . Status == UserStatusMember
}
2017-05-26 12:12:52 +02:00
// IsTrusted : Return true if user is tusted
func ( u * User ) IsTrusted ( ) bool {
2017-05-20 01:10:16 +02:00
return u . Status == UserStatusTrusted
}
2017-05-26 12:12:52 +02:00
// IsModerator : Return true if user is moderator
func ( u * User ) IsModerator ( ) bool {
2017-05-20 01:10:16 +02:00
return u . Status == UserStatusModerator
}
2017-06-07 03:14:57 +02:00
// IsScraped : Return true if user is a scrapped user
func ( u * User ) IsScraped ( ) bool {
return u . Status == UserStatusScraped
2017-06-07 02:59:46 +02:00
}
2017-05-26 12:12:52 +02:00
// GetUnreadNotifications : Get unread notifications from a user
func ( u * User ) GetUnreadNotifications ( ) int {
2017-05-24 09:11:13 +02:00
if u . UnreadNotifications == 0 {
2017-05-20 20:53:05 +02:00
for _ , notif := range u . Notifications {
if ! notif . Read {
u . UnreadNotifications ++
}
}
}
return u . UnreadNotifications
}
This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
// HasAdmin checks that user has an admin permission. Deprecated
func ( u * User ) HasAdmin ( ) bool {
return u . IsModerator ( )
2017-05-06 17:55:02 +02:00
}
This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
// CurrentOrAdmin check that user has admin permission or user is the current user.
func ( u * User ) CurrentOrAdmin ( userID uint ) bool {
log . Debugf ( "user.ID == userID %d %d %s" , u . ID , userID , u . ID == userID )
return ( u . IsModerator ( ) || u . ID == userID )
2017-05-09 07:21:14 +02:00
}
2017-05-10 13:32:45 +02:00
This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
// CurrentUserIdentical check that userID is same as current user's ID.
// TODO: Inline this (won't go do this for us?)
func ( u * User ) CurrentUserIdentical ( userID uint ) bool {
return u . ID == userID
2017-05-10 13:32:45 +02:00
}
This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
// NeedsCaptcha : Check if a user needs captcha
func ( u * User ) NeedsCaptcha ( ) bool {
// Trusted members & Moderators don't
return ! ( u . IsTrusted ( ) || u . IsModerator ( ) )
2017-05-21 18:13:28 +02:00
}
2017-07-02 18:00:12 +02:00
// CanUpload : Check if a user can upload or if upload is enabled in config
func ( u * User ) CanUpload ( ) bool {
if config . Conf . Torrents . UploadsDisabled {
if config . Conf . Torrents . AdminsAreStillAllowedTo && u . IsModerator ( ) {
return true
}
if config . Conf . Torrents . TrustedUsersAreStillAllowedTo && u . IsTrusted ( ) {
return true
}
return false
}
return true
}
This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
// GetRole : Get the status/role of a user
func ( u * User ) GetRole ( ) string {
switch u . Status {
case UserStatusBanned :
return "Banned"
case UserStatusMember :
return "Member"
case UserStatusScraped :
return "Member"
case UserStatusTrusted :
return "Trusted Member"
case UserStatusModerator :
return "Moderator"
}
return "Member"
}
// IsFollower : Check if a user is following another
func ( follower * User ) IsFollower ( u * User ) bool {
var likingUserCount int
ORM . Model ( & UserFollows { } ) . Where ( "user_id = ? and following = ?" , follower . ID , u . ID ) . Count ( & likingUserCount )
return likingUserCount != 0
2017-05-10 13:32:45 +02:00
}
2017-05-10 21:09:37 +02:00
2017-05-26 12:12:52 +02:00
// ToJSON : Conversion of a user model to json
2017-05-10 21:09:37 +02:00
func ( u * User ) ToJSON ( ) UserJSON {
json := UserJSON {
ID : u . ID ,
Username : u . Username ,
2017-06-13 08:01:57 +02:00
APIToken : u . APIToken ,
MD5 : u . MD5 ,
2017-05-10 21:09:37 +02:00
Status : u . Status ,
CreatedAt : u . CreatedAt . Format ( time . RFC3339 ) ,
2017-05-27 00:45:18 +02:00
LikingCount : len ( u . Followers ) ,
LikedCount : len ( u . Likings ) ,
2017-05-10 21:09:37 +02:00
}
return json
}
2017-05-21 18:13:28 +02:00
This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
// GetLikings : Gets who is followed by the user
func ( u * User ) GetLikings ( ) {
var liked [ ] User
ORM . Joins ( "JOIN user_follows on user_follows.following=?" , u . ID ) . Where ( "users.user_id = user_follows.user_id" ) . Group ( "users.user_id" ) . Find ( & liked )
u . Likings = liked
}
// GetFollowers : Gets who is following the user
func ( u * User ) GetFollowers ( ) {
var likings [ ] User
ORM . Joins ( "JOIN user_follows on user_follows.user_id=?" , u . ID ) . Where ( "users.user_id = user_follows.following" ) . Group ( "users.user_id" ) . Find ( & likings )
u . Followers = likings
}
// SetFollow : Makes a user follow another
func ( u * User ) SetFollow ( follower * User ) {
if follower . ID > 0 && u . ID > 0 {
var userFollows = UserFollows { UserID : u . ID , FollowerID : follower . ID }
ORM . Create ( & userFollows )
}
}
// RemoveFollow : Remove a user following another
func ( u * User ) RemoveFollow ( follower * User ) {
if follower . ID > 0 && u . ID > 0 {
var userFollows = UserFollows { UserID : u . ID , FollowerID : follower . ID }
ORM . Delete ( & userFollows )
}
}
/ *
* Old User
* /
// TableName : Return the name of OldComment table
func ( c UserUploadsOld ) TableName ( ) string {
// is this needed here?
return config . Conf . Models . UploadsOldTableName
}
/ *
* User Settings
* /
2017-05-21 18:13:28 +02:00
2017-05-26 12:12:52 +02:00
// Get a user setting by keyname
2017-05-24 09:11:13 +02:00
func ( s * UserSettings ) Get ( key string ) bool {
if val , ok := s . Settings [ key ] ; ok {
return val
2017-05-21 19:38:39 +02:00
}
2017-05-31 04:21:57 +02:00
return config . Conf . Users . DefaultUserSettings [ key ]
2017-05-21 19:38:39 +02:00
}
2017-05-26 12:12:52 +02:00
// GetSettings : get all user settings
2017-05-22 00:22:42 +02:00
func ( s * UserSettings ) GetSettings ( ) map [ string ] bool {
return s . Settings
2017-05-21 18:13:28 +02:00
}
2017-05-26 12:12:52 +02:00
// Set a user setting by keyname
2017-05-22 00:22:42 +02:00
func ( s * UserSettings ) Set ( key string , val bool ) {
if s . Settings == nil {
s . Settings = make ( map [ string ] bool )
}
s . Settings [ key ] = val
}
2017-05-26 12:12:52 +02:00
// ToDefault : Set user settings to default
2017-05-22 00:22:42 +02:00
func ( s * UserSettings ) ToDefault ( ) {
2017-05-31 04:21:57 +02:00
s . Settings = config . Conf . Users . DefaultUserSettings
2017-05-21 18:13:28 +02:00
}
2017-05-26 12:12:52 +02:00
func ( s * UserSettings ) initialize ( ) {
2017-05-22 00:22:42 +02:00
s . Settings = make ( map [ string ] bool )
2017-05-21 18:13:28 +02:00
}
2017-05-26 12:12:52 +02:00
// SaveSettings : Format settings into a json string for preparing before user insertion
2017-05-22 00:22:42 +02:00
func ( u * User ) SaveSettings ( ) {
byteArray , err := json . Marshal ( u . Settings )
2017-05-24 09:11:13 +02:00
if err != nil {
2017-05-22 00:22:42 +02:00
fmt . Print ( err )
}
u . UserSettings = string ( byteArray )
2017-05-21 18:13:28 +02:00
}
2017-05-26 12:12:52 +02:00
// ParseSettings : Function to parse json string into usersettings struct, only parse if necessary
2017-05-22 00:22:42 +02:00
func ( u * User ) ParseSettings ( ) {
2017-05-21 19:38:39 +02:00
if len ( u . Settings . GetSettings ( ) ) == 0 && u . UserSettings != "" {
2017-05-26 12:12:52 +02:00
u . Settings . initialize ( )
2017-05-22 00:22:42 +02:00
json . Unmarshal ( [ ] byte ( u . UserSettings ) , & u . Settings )
} else if len ( u . Settings . GetSettings ( ) ) == 0 && u . UserSettings != "" {
2017-05-26 12:12:52 +02:00
u . Settings . initialize ( )
2017-05-22 00:22:42 +02:00
u . Settings . ToDefault ( )
2017-05-21 19:38:39 +02:00
}
2017-05-24 09:11:13 +02:00
}
2017-07-02 18:00:12 +02:00
// UpdateUserCore updates a user. (Applying the modifed data of user).
2017-07-02 23:53:23 +02:00
func ( u * User ) Update ( ) ( int , error ) {
if u . Email == "" {
u . MD5 = ""
2017-07-02 18:00:12 +02:00
} else {
var err error
2017-07-02 23:53:23 +02:00
u . MD5 , err = crypto . GenerateMD5Hash ( u . Email )
2017-07-02 18:00:12 +02:00
if err != nil {
return http . StatusInternalServerError , err
}
}
2017-07-02 23:53:23 +02:00
u . UpdatedAt = time . Now ( )
err := ORM . Save ( u ) . Error
2017-07-02 18:00:12 +02:00
if err != nil {
return http . StatusInternalServerError , err
}
return http . StatusOK , nil
}
// UpdateRawUser : Function to update a user without updating his associations model
2017-07-02 23:53:23 +02:00
func ( u * User ) UpdateRaw ( ) ( int , error ) {
u . UpdatedAt = time . Now ( )
err := ORM . Model ( u ) . UpdateColumn ( u ) . Error
2017-07-02 18:00:12 +02:00
if err != nil {
return http . StatusInternalServerError , err
}
return http . StatusOK , nil
}
2017-07-02 23:53:23 +02:00
// DeleteUser deletes a user.
func ( u * User ) Delete ( currentUser * User ) ( int , error ) {
if u . ID == 0 {
return http . StatusInternalServerError , errors . New ( "permission_delete_error" )
}
err := ORM . Delete ( u ) . Error
if err != nil {
return http . StatusInternalServerError , errors . New ( "user_not_deleted" )
}
return http . StatusOK , nil
}