Golint friendly next (#756)
* Gofmt friendly Keeping Go source code in line with what they preconize * Golint Friendly Next So I have made some variables unexported Added comments in every function that I know what it does Removed some deprecated stuff that I was sure of Added a comment on possible deprecated methods "Is it deprecated?" Changed some variable/method name according to golint recommendations * Update filelist.go
Cette révision appartient à :
Parent
8fbdeed9f5
révision
6481e90a0c
|
@ -4,7 +4,7 @@ type UserParam struct {
|
|||
Full bool // if true populate Uploads, UsersWeLiked and UsersLikingMe
|
||||
Email string
|
||||
Name string
|
||||
ApiToken string
|
||||
APIToken string
|
||||
ID uint32
|
||||
Max uint32
|
||||
Offset uint32
|
||||
|
|
|
@ -7,8 +7,10 @@ type CacheConfig struct {
|
|||
Size float64
|
||||
}
|
||||
|
||||
// DefaultCacheSize : Size by default for the cache
|
||||
const DefaultCacheSize = 1 << 10
|
||||
|
||||
// DefaultCacheConfig : Config by default for the cache
|
||||
var DefaultCacheConfig = CacheConfig{
|
||||
Dialect: "nop",
|
||||
}
|
||||
|
|
|
@ -12,12 +12,18 @@ import (
|
|||
const (
|
||||
// LastOldTorrentID is the highest torrent ID
|
||||
// that was copied from the original Nyaa
|
||||
LastOldTorrentID = 923000
|
||||
TorrentsTableName = "torrents"
|
||||
ReportsTableName = "torrent_reports"
|
||||
CommentsTableName = "comments"
|
||||
UploadsOldTableName = "user_uploads_old"
|
||||
FilesTableName = "files"
|
||||
LastOldTorrentID = 923000
|
||||
// TorrentsTableName : Name of torrent table in DB
|
||||
TorrentsTableName = "torrents"
|
||||
// ReportsTableName : Name of torrent report table in DB
|
||||
ReportsTableName = "torrent_reports"
|
||||
// CommentsTableName : Name of comments table in DB
|
||||
CommentsTableName = "comments"
|
||||
// UploadsOldTableName : Name of uploads table in DB
|
||||
UploadsOldTableName = "user_uploads_old"
|
||||
// FilesTableName : Name of files table in DB
|
||||
FilesTableName = "files"
|
||||
// NotificationTableName : Name of notifications table in DB
|
||||
NotificationTableName = "notifications"
|
||||
|
||||
// for sukebei:
|
||||
|
@ -29,10 +35,12 @@ const (
|
|||
//FilesTableName = "sukebei_files"
|
||||
)
|
||||
|
||||
// IsSukebei : Tells if we are on the sukebei website
|
||||
func IsSukebei() bool {
|
||||
return TorrentsTableName == "sukebei_torrents"
|
||||
}
|
||||
|
||||
// Config : Configuration for DB, I2P, Fetcher, Go Server and Translation
|
||||
type Config struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
|
@ -55,6 +63,7 @@ type Config struct {
|
|||
I18n I18nConfig `json:"i18n"`
|
||||
}
|
||||
|
||||
// Defaults : Configuration by default
|
||||
var Defaults = Config{"localhost", 9999, "sqlite3", "./nyaa.db?cache_size=50", "default", DefaultScraperConfig, DefaultCacheConfig, DefaultSearchConfig, nil, DefaultMetainfoFetcherConfig, DefaultI18nConfig}
|
||||
|
||||
var allowedDatabaseTypes = map[string]bool{
|
||||
|
@ -70,6 +79,7 @@ var allowedDBLogModes = map[string]bool{
|
|||
"silent": true,
|
||||
}
|
||||
|
||||
// New : Construct a new config variable
|
||||
func New() *Config {
|
||||
var config Config
|
||||
config.Host = Defaults.Host
|
||||
|
@ -112,6 +122,7 @@ func (config *Config) BindFlags() func() error {
|
|||
}
|
||||
}
|
||||
|
||||
// HandleConfFileFlag : Read the config from a file
|
||||
func (config *Config) HandleConfFileFlag(path string) error {
|
||||
if path != "" {
|
||||
file, err := os.Open(path)
|
||||
|
@ -127,30 +138,35 @@ func (config *Config) HandleConfFileFlag(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (config *Config) SetDBType(db_type string) error {
|
||||
if !allowedDatabaseTypes[db_type] {
|
||||
return fmt.Errorf("unknown database backend '%s'", db_type)
|
||||
// SetDBType : Set the DataBase type in config
|
||||
func (config *Config) SetDBType(dbType string) error {
|
||||
if !allowedDatabaseTypes[dbType] {
|
||||
return fmt.Errorf("unknown database backend '%s'", dbType)
|
||||
}
|
||||
config.DBType = db_type
|
||||
config.DBType = dbType
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *Config) SetDBLogMode(db_logmode string) error {
|
||||
if !allowedDBLogModes[db_logmode] {
|
||||
return fmt.Errorf("unknown database log mode '%s'", db_logmode)
|
||||
// SetDBLogMode : Set the log mode in config
|
||||
func (config *Config) SetDBLogMode(dbLogmode string) error {
|
||||
if !allowedDBLogModes[dbLogmode] {
|
||||
return fmt.Errorf("unknown database log mode '%s'", dbLogmode)
|
||||
}
|
||||
config.DBLogMode = db_logmode
|
||||
config.DBLogMode = dbLogmode
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read : Decode config from json to config
|
||||
func (config *Config) Read(input io.Reader) error {
|
||||
return json.NewDecoder(input).Decode(config)
|
||||
}
|
||||
|
||||
// Write : Encode config from json to config
|
||||
func (config *Config) Write(output io.Writer) error {
|
||||
return json.NewEncoder(output).Encode(config)
|
||||
}
|
||||
|
||||
// Pretty : Write config json in a file
|
||||
func (config *Config) Pretty(output io.Writer) error {
|
||||
data, err := json.MarshalIndent(config, "", "\t")
|
||||
if err != nil {
|
||||
|
|
|
@ -6,14 +6,23 @@ import "time"
|
|||
// Future hosts shouldn't have to rebuild the binary to update a setting
|
||||
|
||||
const (
|
||||
SendEmail = true
|
||||
EmailFrom = "donotrespond@nyaa.pantsu.cat"
|
||||
EmailTestTo = ""
|
||||
EmailHost = "localhost"
|
||||
// SendEmail : Enable Email
|
||||
SendEmail = true
|
||||
// EmailFrom : email address by default
|
||||
EmailFrom = "donotrespond@nyaa.pantsu.cat"
|
||||
// EmailTestTo : when testing to who send email
|
||||
EmailTestTo = ""
|
||||
// EmailHost : Host of mail server
|
||||
EmailHost = "localhost"
|
||||
// EmailUsername : Username needed for the connection
|
||||
EmailUsername = ""
|
||||
// EmailPassword : Password needed for the connection
|
||||
EmailPassword = ""
|
||||
EmailPort = 465
|
||||
EmailTimeout = 10 * time.Second
|
||||
// EmailPort : Mail Server port
|
||||
EmailPort = 465
|
||||
// EmailTimeout : Timeout for waiting server response
|
||||
EmailTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// EmailTokenHashKey : /!\ Email hash for generating email activation token /!\
|
||||
var EmailTokenHashKey = []byte("CHANGE_THIS_BEFORE_DEPLOYING_YOU_GIT")
|
||||
|
|
|
@ -5,7 +5,9 @@ package config
|
|||
|
||||
const (
|
||||
// Environment should be one of: DEVELOPMENT, TEST, PRODUCTION
|
||||
Environment = "DEVELOPMENT"
|
||||
WebAddress = "nyaa.pantsu.cat"
|
||||
Environment = "DEVELOPMENT"
|
||||
// WebAddress : url of the website
|
||||
WebAddress = "nyaa.pantsu.cat"
|
||||
// AuthTokenExpirationDay : Number of Days for token expiration when logged in
|
||||
AuthTokenExpirationDay = 1000
|
||||
)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package config
|
||||
|
||||
// I18nConfig : Config struct for translation
|
||||
type I18nConfig struct {
|
||||
TranslationsDirectory string `json:"translations_directory"`
|
||||
DefaultLanguage string `json:"default_language"`
|
||||
}
|
||||
|
||||
// DefaultI18nConfig : Default configuration for translation
|
||||
var DefaultI18nConfig = I18nConfig{
|
||||
TranslationsDirectory: "translations",
|
||||
DefaultLanguage: "en-us",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package config
|
||||
|
||||
// I2PConfig : Config struct for I2P
|
||||
type I2PConfig struct {
|
||||
Name string `json:"name"`
|
||||
Addr string `json:"samaddr"`
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
package config
|
||||
|
||||
const (
|
||||
AccessLogFilePath = "log/access"
|
||||
// AccessLogFilePath : Path to logs access
|
||||
AccessLogFilePath = "log/access"
|
||||
// AccessLogFileExtension : Extension for log file
|
||||
AccessLogFileExtension = ".txt"
|
||||
AccessLogMaxSize = 5 // megabytes
|
||||
AccessLogMaxBackups = 7
|
||||
AccessLogMaxAge = 30 //days
|
||||
ErrorLogFilePath = "log/error"
|
||||
ErrorLogFileExtension = ".json"
|
||||
ErrorLogMaxSize = 10 // megabytes
|
||||
ErrorLogMaxBackups = 7
|
||||
ErrorLogMaxAge = 30 //days
|
||||
// AccessLogMaxSize : Size max for a log file in megabytes
|
||||
AccessLogMaxSize = 5
|
||||
// AccessLogMaxBackups : Number of file for logs
|
||||
AccessLogMaxBackups = 7
|
||||
// AccessLogMaxAge : Number of days that we keep logs
|
||||
AccessLogMaxAge = 30
|
||||
// ErrorLogFilePath : Path to log errors
|
||||
ErrorLogFilePath = "log/error"
|
||||
// ErrorLogFileExtension : Extension for log file
|
||||
ErrorLogFileExtension = ".json"
|
||||
// ErrorLogMaxSize : Size max for a log file in megabytes
|
||||
ErrorLogMaxSize = 10
|
||||
// ErrorLogMaxBackups : Number of file for logs
|
||||
ErrorLogMaxBackups = 7
|
||||
// ErrorLogMaxAge : Number of days that we keep logs
|
||||
ErrorLogMaxAge = 30
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package config
|
||||
|
||||
// MetainfoFetcherConfig : Config struct for metainfo fetcher
|
||||
type MetainfoFetcherConfig struct {
|
||||
QueueSize int `json:"queue_size"`
|
||||
Timeout int `json:"timeout"`
|
||||
|
@ -14,6 +15,7 @@ type MetainfoFetcherConfig struct {
|
|||
FetchNewTorrentsOnly bool `json:"fetch_new_torrents_only"`
|
||||
}
|
||||
|
||||
// DefaultMetainfoFetcherConfig : Default configuration for metainfofetcher
|
||||
var DefaultMetainfoFetcherConfig = MetainfoFetcherConfig{
|
||||
QueueSize: 10,
|
||||
Timeout: 120, // 2 min
|
||||
|
|
|
@ -4,6 +4,7 @@ package config
|
|||
// Future hosts shouldn't have to rebuild the binary to update a setting
|
||||
|
||||
const (
|
||||
// TorrentsPerPage : Number of torrents per page
|
||||
TorrentsPerPage = 50
|
||||
MaxTorrentsPerPage = 300
|
||||
)
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package config
|
||||
|
||||
// ScrapeConfig : Config struct for Scraping
|
||||
type ScrapeConfig struct {
|
||||
URL string `json:"scrape_url"`
|
||||
Name string `json:"name"`
|
||||
IntervalSeconds int64 `json:"interval"`
|
||||
}
|
||||
|
||||
// ScraperConfig : Config struct for Scraper
|
||||
type ScraperConfig struct {
|
||||
Addr string `json:"bind"`
|
||||
NumWorkers int `json:"workers"`
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package config
|
||||
|
||||
// SearchConfig : Config struct for search
|
||||
// Is it deprecated?
|
||||
type SearchConfig struct {
|
||||
}
|
||||
|
||||
// DefaultSearchConfig : Default config for search
|
||||
var DefaultSearchConfig = SearchConfig{}
|
||||
|
||||
const (
|
||||
|
|
|
@ -4,6 +4,8 @@ package config
|
|||
// Future hosts shouldn't have to rebuild the binary to update a setting
|
||||
|
||||
const (
|
||||
// TorrentOrder : Default sorting field for torrents
|
||||
TorrentOrder = "torrent_id"
|
||||
TorrentSort = "DESC"
|
||||
// TorrentSort : Default sorting order for torrents
|
||||
TorrentSort = "DESC"
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package config
|
||||
|
||||
/* Config of different status id for torrents */
|
||||
// TorrentStatus : Config of different status id for torrents
|
||||
var TorrentStatus = map[int]bool{
|
||||
0: true,
|
||||
1: true,
|
||||
|
@ -9,7 +9,7 @@ var TorrentStatus = map[int]bool{
|
|||
4: true,
|
||||
}
|
||||
|
||||
/* Config for Sukebei categories */
|
||||
// TorrentSukebeiCategories : Config for Sukebei categories
|
||||
var TorrentSukebeiCategories = map[string]string{
|
||||
"1_": "art",
|
||||
"1_1": "art_anime",
|
||||
|
@ -22,7 +22,7 @@ var TorrentSukebeiCategories = map[string]string{
|
|||
"2_2": "real_life_videos",
|
||||
}
|
||||
|
||||
/* Config for Site categories */
|
||||
// TorrentCleanCategories : Config for Site categories
|
||||
var TorrentCleanCategories = map[string]string{
|
||||
"3_": "anime",
|
||||
"3_12": "anime_amv",
|
||||
|
|
|
@ -2,6 +2,7 @@ package config
|
|||
|
||||
// TODO: Update FAQ template to use this variable
|
||||
|
||||
// Trackers : Default trackers supported
|
||||
var Trackers = []string{
|
||||
"udp://tracker.doko.moe:6969",
|
||||
"udp://tracker.coppersurfer.tk:6969",
|
||||
|
|
|
@ -3,12 +3,20 @@ package config
|
|||
const (
|
||||
// TorrentFileStorage = "/var/www/wherever/you/want"
|
||||
// TorrentStorageLink = "https://your.site/somewhere/%s.torrent"
|
||||
|
||||
// TorrentFileStorage : Path to default torrent storage location
|
||||
TorrentFileStorage = ""
|
||||
// TorrentStorageLink : Url of torrent file download location
|
||||
TorrentStorageLink = ""
|
||||
|
||||
// TODO: deprecate this and move all files to the same server
|
||||
TorrentCacheLink = "http://anicache.com/torrent/%s.torrent"
|
||||
UploadsDisabled = false
|
||||
AdminsAreStillAllowedTo = true
|
||||
|
||||
// TorrentCacheLink : Url of torrent site cache
|
||||
TorrentCacheLink = "http://anicache.com/torrent/%s.torrent"
|
||||
// UploadsDisabled : Disable uploads for everyone except below
|
||||
UploadsDisabled = false
|
||||
// AdminsAreStillAllowedTo : Enable admin torrent upload even if UploadsDisabled is true
|
||||
AdminsAreStillAllowedTo = true
|
||||
// TrustedUsersAreStillAllowedTo : Enable trusted users torrent upload even if UploadsDisabled is true
|
||||
TrustedUsersAreStillAllowedTo = true
|
||||
)
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package config
|
||||
|
||||
/*
|
||||
* Here we config the notifications options
|
||||
// DefaultUserSettings :
|
||||
/* Here we config the notifications options
|
||||
* Uses in user model for default setting
|
||||
* Be aware, default values in user update form are
|
||||
* in service/user/form/form_validator.go
|
||||
*/
|
||||
|
||||
var DefaultUserSettings = map[string]bool{
|
||||
"new_torrent": true,
|
||||
"new_torrent_email": false,
|
||||
|
|
|
@ -72,22 +72,24 @@ type Database interface {
|
|||
// XXX: add more as needed
|
||||
}
|
||||
|
||||
var ErrInvalidDatabaseDialect = errors.New("invalid database dialect")
|
||||
var ErrSqliteSucksAss = errors.New("sqlite3 sucks ass so it's not supported yet")
|
||||
var errInvalidDatabaseDialect = errors.New("invalid database dialect")
|
||||
var errSqliteSucksAss = errors.New("sqlite3 sucks ass so it's not supported yet")
|
||||
|
||||
// Impl : Database variable
|
||||
var Impl Database
|
||||
|
||||
// Configure : Configure Database
|
||||
func Configure(conf *config.Config) (err error) {
|
||||
switch conf.DBType {
|
||||
case "postgres":
|
||||
Impl, err = postgres.New(conf.DBParams)
|
||||
break
|
||||
case "sqlite3":
|
||||
err = ErrSqliteSucksAss
|
||||
err = errSqliteSucksAss
|
||||
// Impl, err = sqlite.New(conf.DBParams)
|
||||
break
|
||||
default:
|
||||
err = ErrInvalidDatabaseDialect
|
||||
err = errInvalidDatabaseDialect
|
||||
}
|
||||
if err == nil {
|
||||
log.Infof("Init %s database", conf.DBType)
|
||||
|
|
|
@ -5,16 +5,19 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/model"
|
||||
)
|
||||
|
||||
// InsertComment : Insert a comment
|
||||
func (db *Database) InsertComment(comment *model.Comment) (err error) {
|
||||
_, err = db.getPrepared(queryInsertComment).Exec(comment.ID, comment.TorrentID, comment.Content, comment.CreatedAt)
|
||||
return
|
||||
}
|
||||
|
||||
// GetCommentsWhere : Get comments on condition
|
||||
func (db *Database) GetCommentsWhere(param *common.CommentParam) (comments []model.Comment, err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteCommentsWhere : Delete comments on condition
|
||||
func (db *Database) DeleteCommentsWhere(param *common.CommentParam) (deleted uint32, err error) {
|
||||
|
||||
return
|
||||
|
|
|
@ -47,7 +47,7 @@ func scanTorrentReportColumnsFull(rows *sql.Rows, r *model.TorrentReport) {
|
|||
const userSelectColumnsFull = `user_id, username, password, email, status, created_at, updated_at, api_token, api_token_expiry, language, md5`
|
||||
|
||||
func scanUserColumnsFull(rows *sql.Rows, u *model.User) {
|
||||
rows.Scan(&u.ID, &u.Username, &u.Password, &u.Email, &u.Status, &u.CreatedAt, &u.UpdatedAt, &u.ApiToken, &u.ApiTokenExpiry, &u.Language, &u.MD5)
|
||||
rows.Scan(&u.ID, &u.Username, &u.Password, &u.Email, &u.Status, &u.CreatedAt, &u.UpdatedAt, &u.APIToken, &u.APITokenExpiry, &u.Language, &u.MD5)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -82,12 +82,12 @@ func (db *Database) GetUserByID(id uint32) (user model.User, has bool, err error
|
|||
}
|
||||
|
||||
func (db *Database) InsertUser(u *model.User) (err error) {
|
||||
_, err = db.getPrepared(queryInsertUser).Exec(u.Username, u.Password, u.Email, u.Status, u.CreatedAt, u.UpdatedAt, u.ApiToken, u.ApiTokenExpiry, u.Language, u.MD5)
|
||||
_, err = db.getPrepared(queryInsertUser).Exec(u.Username, u.Password, u.Email, u.Status, u.CreatedAt, u.UpdatedAt, u.APIToken, u.APITokenExpiry, u.Language, u.MD5)
|
||||
return
|
||||
}
|
||||
|
||||
func (db *Database) UpdateUser(u *model.User) (err error) {
|
||||
_, err = db.getPrepared(queryUpdateUser).Exec(u.ID, u.Username, u.Password, u.Email, u.Status, u.UpdatedAt, u.ApiToken, u.ApiTokenExpiry, u.Language, u.MD5)
|
||||
_, err = db.getPrepared(queryUpdateUser).Exec(u.ID, u.Username, u.Password, u.Email, u.Status, u.UpdatedAt, u.APIToken, u.APITokenExpiry, u.Language, u.MD5)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -101,8 +101,8 @@ func (db *Database) GetUsersWhere(param *common.UserParam) (users []model.User,
|
|||
if has {
|
||||
users = append(users, user)
|
||||
}
|
||||
} else if len(param.ApiToken) > 0 {
|
||||
user, has, err = db.GetUserByAPIToken(param.ApiToken)
|
||||
} else if len(param.APIToken) > 0 {
|
||||
user, has, err = db.GetUserByAPIToken(param.APIToken)
|
||||
if has {
|
||||
users = append(users, user)
|
||||
}
|
||||
|
@ -141,9 +141,9 @@ func (db *Database) DeleteUsersWhere(param *common.UserParam) (deleted uint32, e
|
|||
} else if len(param.Email) > 0 {
|
||||
queryName = queryDeleteUserByEmail
|
||||
p = param.Email
|
||||
} else if len(param.ApiToken) > 0 {
|
||||
} else if len(param.APIToken) > 0 {
|
||||
queryName = queryDeleteUserByToken
|
||||
p = param.ApiToken
|
||||
p = param.APIToken
|
||||
} else {
|
||||
// delete nothing
|
||||
return
|
||||
|
|
|
@ -2,7 +2,8 @@ package sqlite
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3" // Need for sqlite
|
||||
)
|
||||
|
||||
// queryEvent is a queued event to be executed in a pipeline to ensure that sqlite access is done from 1 goroutine
|
||||
|
@ -12,6 +13,7 @@ type queryEvent struct {
|
|||
handleResult func(*sql.Rows, error)
|
||||
}
|
||||
|
||||
// New : Create a new database
|
||||
func New(param string) (db *Database, err error) {
|
||||
db = new(Database)
|
||||
db.conn, err = sql.Open("sqlite3", param)
|
||||
|
@ -23,6 +25,7 @@ func New(param string) (db *Database, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Database structure
|
||||
type Database struct {
|
||||
conn *sql.DB
|
||||
query chan *queryEvent
|
||||
|
|
|
@ -5,19 +5,22 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/model"
|
||||
"github.com/NyaaPantsu/nyaa/util/log"
|
||||
"github.com/azhao12345/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres" // Need for postgres support
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite" // Need for sqlite
|
||||
)
|
||||
|
||||
// Logger interface
|
||||
type Logger interface {
|
||||
Print(v ...interface{})
|
||||
}
|
||||
|
||||
// use the default gorm logger that prints to stdout
|
||||
// DefaultLogger : use the default gorm logger that prints to stdout
|
||||
var DefaultLogger Logger = nil
|
||||
|
||||
// ORM : Variable for interacting with database
|
||||
var ORM *gorm.DB
|
||||
|
||||
// IsSqlite : Variable to know if we are in sqlite or postgres
|
||||
var IsSqlite bool
|
||||
|
||||
// GormInit init gorm ORM.
|
||||
|
|
|
@ -8,44 +8,49 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Generates Atom feed as XML
|
||||
|
||||
// ns : Generates Atom feed as XML
|
||||
const ns = "http://www.w3.org/2005/Atom"
|
||||
|
||||
// AtomPerson struct
|
||||
type AtomPerson struct {
|
||||
Name string `xml:"name,omitempty"`
|
||||
Uri string `xml:"uri,omitempty"`
|
||||
URI string `xml:"uri,omitempty"`
|
||||
Email string `xml:"email,omitempty"`
|
||||
}
|
||||
|
||||
// AtomSummary struct
|
||||
type AtomSummary struct {
|
||||
XMLName xml.Name `xml:"summary"`
|
||||
Content string `xml:",chardata"`
|
||||
Type string `xml:"type,attr"`
|
||||
}
|
||||
|
||||
// AtomContent struct
|
||||
type AtomContent struct {
|
||||
XMLName xml.Name `xml:"content"`
|
||||
Content string `xml:",chardata"`
|
||||
Type string `xml:"type,attr"`
|
||||
}
|
||||
|
||||
// AtomAuthor struct
|
||||
type AtomAuthor struct {
|
||||
XMLName xml.Name `xml:"author"`
|
||||
AtomPerson
|
||||
}
|
||||
|
||||
// AtomContributor struct
|
||||
type AtomContributor struct {
|
||||
XMLName xml.Name `xml:"contributor"`
|
||||
AtomPerson
|
||||
}
|
||||
|
||||
// AtomEntry struct
|
||||
type AtomEntry struct {
|
||||
XMLName xml.Name `xml:"entry"`
|
||||
Xmlns string `xml:"xmlns,attr,omitempty"`
|
||||
Title string `xml:"title"` // required
|
||||
Updated string `xml:"updated"` // required
|
||||
Id string `xml:"id"` // required
|
||||
ID string `xml:"id"` // required
|
||||
Category string `xml:"category,omitempty"`
|
||||
Content *AtomContent
|
||||
Rights string `xml:"rights,omitempty"`
|
||||
|
@ -57,6 +62,7 @@ type AtomEntry struct {
|
|||
Author *AtomAuthor // required if feed lacks an author
|
||||
}
|
||||
|
||||
// AtomLink struct
|
||||
type AtomLink struct {
|
||||
//Atom 1.0 <link rel="enclosure" type="audio/mpeg" title="MP3" href="http://www.example.org/myaudiofile.mp3" length="1234" />
|
||||
XMLName xml.Name `xml:"link"`
|
||||
|
@ -66,11 +72,12 @@ type AtomLink struct {
|
|||
Length string `xml:"length,attr,omitempty"`
|
||||
}
|
||||
|
||||
// AtomFeed struct
|
||||
type AtomFeed struct {
|
||||
XMLName xml.Name `xml:"feed"`
|
||||
Xmlns string `xml:"xmlns,attr"`
|
||||
Title string `xml:"title"` // required
|
||||
Id string `xml:"id"` // required
|
||||
ID string `xml:"id"` // required
|
||||
Updated string `xml:"updated"` // required
|
||||
Category string `xml:"category,omitempty"`
|
||||
Icon string `xml:"icon,omitempty"`
|
||||
|
@ -83,12 +90,13 @@ type AtomFeed struct {
|
|||
Entries []*AtomEntry
|
||||
}
|
||||
|
||||
// Atom struct
|
||||
type Atom struct {
|
||||
*Feed
|
||||
}
|
||||
|
||||
func newAtomEntry(i *Item) *AtomEntry {
|
||||
id := i.Id
|
||||
id := i.ID
|
||||
// assume the description is html
|
||||
c := &AtomContent{Content: i.Description, Type: "html"}
|
||||
|
||||
|
@ -114,7 +122,7 @@ func newAtomEntry(i *Item) *AtomEntry {
|
|||
Title: i.Title,
|
||||
Link: &AtomLink{Href: i.Link.Href, Rel: i.Link.Rel, Type: i.Link.Type},
|
||||
Content: c,
|
||||
Id: id,
|
||||
ID: id,
|
||||
Updated: anyTimeFormat(time.RFC3339, i.Updated, i.Created),
|
||||
}
|
||||
|
||||
|
@ -131,7 +139,7 @@ func newAtomEntry(i *Item) *AtomEntry {
|
|||
return x
|
||||
}
|
||||
|
||||
// create a new AtomFeed with a generic Feed struct's data
|
||||
// AtomFeed : create a new AtomFeed with a generic Feed struct's data
|
||||
func (a *Atom) AtomFeed() *AtomFeed {
|
||||
updated := anyTimeFormat(time.RFC3339, a.Updated, a.Created)
|
||||
feed := &AtomFeed{
|
||||
|
@ -139,7 +147,7 @@ func (a *Atom) AtomFeed() *AtomFeed {
|
|||
Title: a.Title,
|
||||
Link: &AtomLink{Href: a.Link.Href, Rel: a.Link.Rel},
|
||||
Subtitle: a.Description,
|
||||
Id: a.Link.Href,
|
||||
ID: a.Link.Href,
|
||||
Updated: updated,
|
||||
Rights: a.Copyright,
|
||||
}
|
||||
|
@ -152,12 +160,12 @@ func (a *Atom) AtomFeed() *AtomFeed {
|
|||
return feed
|
||||
}
|
||||
|
||||
// return an XML-Ready object for an Atom object
|
||||
func (a *Atom) FeedXml() interface{} {
|
||||
// FeedXML : return an XML-Ready object for an Atom object
|
||||
func (a *Atom) FeedXML() interface{} {
|
||||
return a.AtomFeed()
|
||||
}
|
||||
|
||||
// return an XML-ready object for an AtomFeed object
|
||||
func (a *AtomFeed) FeedXml() interface{} {
|
||||
// FeedXML : return an XML-ready object for an AtomFeed object
|
||||
func (a *AtomFeed) FeedXML() interface{} {
|
||||
return a
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/*
|
||||
Syndication (feed) generator library for golang.
|
||||
// Package feeds :
|
||||
package feeds
|
||||
|
||||
/* Syndication (feed) generator library for golang.
|
||||
|
||||
Installing
|
||||
|
||||
|
@ -67,4 +69,3 @@ From here, you can modify or add each syndication's specific fields before outpu
|
|||
rss, err := ToXML(rssFeed)
|
||||
|
||||
*/
|
||||
package feeds
|
||||
|
|
|
@ -6,15 +6,17 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Link Struct
|
||||
type Link struct {
|
||||
Href, Rel, Type, Length string
|
||||
}
|
||||
|
||||
// Author Struct
|
||||
type Author struct {
|
||||
Name, Email string
|
||||
}
|
||||
|
||||
// modified for Nyaa
|
||||
// Torrent Struct modified for Nyaa
|
||||
type Torrent struct {
|
||||
FileName string
|
||||
Seeds uint32
|
||||
|
@ -24,18 +26,20 @@ type Torrent struct {
|
|||
MagnetURI string
|
||||
}
|
||||
|
||||
// Item Struct
|
||||
type Item struct {
|
||||
Title string
|
||||
Link *Link
|
||||
Author *Author
|
||||
Description string // used as description in rss, summary in atom
|
||||
Id string // used as guid in rss, id in atom
|
||||
ID string // used as guid in rss, id in atom
|
||||
Updated time.Time
|
||||
Created time.Time
|
||||
|
||||
Torrent *Torrent // modified for Nyaa
|
||||
}
|
||||
|
||||
// Feed Struct
|
||||
type Feed struct {
|
||||
Title string
|
||||
Link *Link
|
||||
|
@ -43,13 +47,13 @@ type Feed struct {
|
|||
Author *Author
|
||||
Updated time.Time
|
||||
Created time.Time
|
||||
Id string
|
||||
ID string
|
||||
Subtitle string
|
||||
Items []*Item
|
||||
Copyright string
|
||||
}
|
||||
|
||||
// add a new Item to a Feed
|
||||
// Add a new Item to a Feed
|
||||
func (f *Feed) Add(item *Item) {
|
||||
f.Items = append(f.Items, item)
|
||||
}
|
||||
|
@ -64,15 +68,15 @@ func anyTimeFormat(format string, times ...time.Time) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// interface used by ToXML to get a object suitable for exporting XML.
|
||||
type XmlFeed interface {
|
||||
FeedXml() interface{}
|
||||
// XMLFeed : interface used by ToXML to get a object suitable for exporting XML.
|
||||
type XMLFeed interface {
|
||||
FeedXML() interface{}
|
||||
}
|
||||
|
||||
// turn a feed object (either a Feed, AtomFeed, or RssFeed) into xml
|
||||
// ToXML : turn a feed object (either a Feed, AtomFeed, or RssFeed) into xml
|
||||
// returns an error if xml marshaling fails
|
||||
func ToXML(feed XmlFeed) (string, error) {
|
||||
x := feed.FeedXml()
|
||||
func ToXML(feed XMLFeed) (string, error) {
|
||||
x := feed.FeedXML()
|
||||
data, err := xml.MarshalIndent(x, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -82,10 +86,10 @@ func ToXML(feed XmlFeed) (string, error) {
|
|||
return s, nil
|
||||
}
|
||||
|
||||
// Write a feed object (either a Feed, AtomFeed, or RssFeed) as XML into
|
||||
// WriteXML : Write a feed object (either a Feed, AtomFeed, or RssFeed) as XML into
|
||||
// the writer. Returns an error if XML marshaling fails.
|
||||
func WriteXML(feed XmlFeed, w io.Writer) error {
|
||||
x := feed.FeedXml()
|
||||
func WriteXML(feed XMLFeed, w io.Writer) error {
|
||||
x := feed.FeedXML()
|
||||
// write default xml header, without the newline
|
||||
if _, err := w.Write([]byte(xml.Header[:len(xml.Header)-1])); err != nil {
|
||||
return err
|
||||
|
@ -95,24 +99,24 @@ func WriteXML(feed XmlFeed, w io.Writer) error {
|
|||
return e.Encode(x)
|
||||
}
|
||||
|
||||
// creates an Atom representation of this feed
|
||||
// ToAtom : creates an Atom representation of this feed
|
||||
func (f *Feed) ToAtom() (string, error) {
|
||||
a := &Atom{f}
|
||||
return ToXML(a)
|
||||
}
|
||||
|
||||
// Writes an Atom representation of this feed to the writer.
|
||||
// WriteAtom : Writes an Atom representation of this feed to the writer.
|
||||
func (f *Feed) WriteAtom(w io.Writer) error {
|
||||
return WriteXML(&Atom{f}, w)
|
||||
}
|
||||
|
||||
// creates an Rss representation of this feed
|
||||
// ToRss : creates an Rss representation of this feed
|
||||
func (f *Feed) ToRss() (string, error) {
|
||||
r := &Rss{f}
|
||||
return ToXML(r)
|
||||
}
|
||||
|
||||
// Writes an RSS representation of this feed to the writer.
|
||||
// WriteRss : Writes an RSS representation of this feed to the writer.
|
||||
func (f *Feed) WriteRss(w io.Writer) error {
|
||||
return WriteXML(&Rss{f}, w)
|
||||
}
|
||||
|
|
34
feeds/rss.go
34
feeds/rss.go
|
@ -12,22 +12,24 @@ import (
|
|||
)
|
||||
|
||||
// private wrapper around the RssFeed which gives us the <rss>..</rss> xml
|
||||
type rssFeedXml struct {
|
||||
type rssFeedXML struct {
|
||||
XMLName xml.Name `xml:"rss"`
|
||||
Version string `xml:"version,attr"`
|
||||
Channel *RssFeed
|
||||
XMLNSTorrent string `xml:"xmlns:torrent,attr"` // modified for Nyaa
|
||||
}
|
||||
|
||||
// RssImage Struct
|
||||
type RssImage struct {
|
||||
XMLName xml.Name `xml:"image"`
|
||||
Url string `xml:"url"`
|
||||
URL string `xml:"url"`
|
||||
Title string `xml:"title"`
|
||||
Link string `xml:"link"`
|
||||
Width int `xml:"width,omitempty"`
|
||||
Height int `xml:"height,omitempty"`
|
||||
}
|
||||
|
||||
// RssTextInput Struct
|
||||
type RssTextInput struct {
|
||||
XMLName xml.Name `xml:"textInput"`
|
||||
Title string `xml:"title"`
|
||||
|
@ -36,6 +38,7 @@ type RssTextInput struct {
|
|||
Link string `xml:"link"`
|
||||
}
|
||||
|
||||
// RssFeed Struct
|
||||
type RssFeed struct {
|
||||
XMLName xml.Name `xml:"channel"`
|
||||
Title string `xml:"title"` // required
|
||||
|
@ -51,7 +54,7 @@ type RssFeed struct {
|
|||
Generator string `xml:"generator,omitempty"`
|
||||
Docs string `xml:"docs,omitempty"`
|
||||
Cloud string `xml:"cloud,omitempty"`
|
||||
Ttl int `xml:"ttl,omitempty"`
|
||||
TTL int `xml:"ttl,omitempty"`
|
||||
Rating string `xml:"rating,omitempty"`
|
||||
SkipHours string `xml:"skipHours,omitempty"`
|
||||
SkipDays string `xml:"skipDays,omitempty"`
|
||||
|
@ -60,6 +63,7 @@ type RssFeed struct {
|
|||
Items []*RssItem
|
||||
}
|
||||
|
||||
// RssItem Struct
|
||||
type RssItem struct {
|
||||
XMLName xml.Name `xml:"item"`
|
||||
Title string `xml:"title"` // required
|
||||
|
@ -69,7 +73,7 @@ type RssItem struct {
|
|||
Category string `xml:"category,omitempty"`
|
||||
Comments string `xml:"comments,omitempty"`
|
||||
Enclosure *RssEnclosure
|
||||
Guid string `xml:"guid,omitempty"` // Id used
|
||||
GUID string `xml:"guid,omitempty"` // Id used
|
||||
PubDate string `xml:"pubDate,omitempty"` // created or updated
|
||||
Source string `xml:"source,omitempty"`
|
||||
|
||||
|
@ -82,14 +86,16 @@ type RssItem struct {
|
|||
MagnetURI string `xml:"torrent:magnetURI"`
|
||||
}
|
||||
|
||||
// RssEnclosure Struct
|
||||
type RssEnclosure struct {
|
||||
//RSS 2.0 <enclosure url="http://example.com/file.mp3" length="123456789" type="audio/mpeg" />
|
||||
XMLName xml.Name `xml:"enclosure"`
|
||||
Url string `xml:"url,attr"`
|
||||
URL string `xml:"url,attr"`
|
||||
Length string `xml:"length,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
}
|
||||
|
||||
// Rss Struct
|
||||
type Rss struct {
|
||||
*Feed
|
||||
}
|
||||
|
@ -100,7 +106,7 @@ func newRssItem(i *Item) *RssItem {
|
|||
Title: i.Title,
|
||||
Link: i.Link.Href,
|
||||
Description: i.Description,
|
||||
Guid: i.Id,
|
||||
GUID: i.ID,
|
||||
PubDate: anyTimeFormat(time.RFC1123Z, i.Created, i.Updated),
|
||||
// modified for Nyaa
|
||||
FileName: i.Torrent.FileName,
|
||||
|
@ -114,7 +120,7 @@ func newRssItem(i *Item) *RssItem {
|
|||
intLength, err := strconv.ParseInt(i.Link.Length, 10, 64)
|
||||
|
||||
if err == nil && (intLength > 0 || i.Link.Type != "") {
|
||||
item.Enclosure = &RssEnclosure{Url: i.Link.Href, Type: i.Link.Type, Length: i.Link.Length}
|
||||
item.Enclosure = &RssEnclosure{URL: i.Link.Href, Type: i.Link.Type, Length: i.Link.Length}
|
||||
}
|
||||
if i.Author != nil {
|
||||
item.Author = i.Author.Name
|
||||
|
@ -122,7 +128,7 @@ func newRssItem(i *Item) *RssItem {
|
|||
return item
|
||||
}
|
||||
|
||||
// create a new RssFeed with a generic Feed struct's data
|
||||
// RssFeed : create a new RssFeed with a generic Feed struct's data
|
||||
func (r *Rss) RssFeed() *RssFeed {
|
||||
pub := anyTimeFormat(time.RFC1123Z, r.Created, r.Updated)
|
||||
build := anyTimeFormat(time.RFC1123Z, r.Updated)
|
||||
|
@ -149,15 +155,15 @@ func (r *Rss) RssFeed() *RssFeed {
|
|||
return channel
|
||||
}
|
||||
|
||||
// return an XML-Ready object for an Rss object
|
||||
func (r *Rss) FeedXml() interface{} {
|
||||
// FeedXML : return an XML-Ready object for an Rss object
|
||||
func (r *Rss) FeedXML() interface{} {
|
||||
// only generate version 2.0 feeds for now
|
||||
return r.RssFeed().FeedXml()
|
||||
return r.RssFeed().FeedXML()
|
||||
|
||||
}
|
||||
|
||||
// return an XML-ready object for an RssFeed object
|
||||
func (r *RssFeed) FeedXml() interface{} {
|
||||
// FeedXML : return an XML-ready object for an RssFeed object
|
||||
func (r *RssFeed) FeedXML() interface{} {
|
||||
// modified for Nyaa
|
||||
return &rssFeedXml{Version: "2.0", Channel: r, XMLNSTorrent: "http://xmlns.nyaa.pantsu.cat/torrent/"}
|
||||
return &rssFeedXML{Version: "2.0", Channel: r, XMLNSTorrent: "http://xmlns.nyaa.pantsu.cat/torrent/"}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,10 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// UUID type
|
||||
type UUID [16]byte
|
||||
|
||||
// create a new uuid v4
|
||||
// NewUUID : create a new uuid v4
|
||||
func NewUUID() *UUID {
|
||||
u := &UUID{}
|
||||
_, err := rand.Read(u[:16])
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/config"
|
||||
)
|
||||
|
||||
// Comment model
|
||||
type Comment struct {
|
||||
ID uint `gorm:"column:comment_id;primary_key"`
|
||||
TorrentID uint `gorm:"column:torrent_id"`
|
||||
|
@ -19,19 +20,22 @@ type Comment struct {
|
|||
User *User `gorm:"AssociationForeignKey:UserID;ForeignKey:user_id"`
|
||||
}
|
||||
|
||||
// Returns the total size of memory recursively allocated for this struct
|
||||
// Size : Returns the total size of memory recursively allocated for this struct
|
||||
func (c Comment) Size() int {
|
||||
return (3 + 3*3 + 2 + 2 + len(c.Content)) * 8
|
||||
}
|
||||
|
||||
// TableName : Return the name of comment table
|
||||
func (c Comment) TableName() string {
|
||||
return config.CommentsTableName
|
||||
}
|
||||
|
||||
// Identifier : Return the identifier of the comment
|
||||
func (c *Comment) Identifier() string { // We Can personalize the identifier but we would have to handle toggle read in that case
|
||||
return c.Torrent.Identifier()
|
||||
}
|
||||
|
||||
// OldComment model from old nyaa
|
||||
type OldComment struct {
|
||||
TorrentID uint `gorm:"column:torrent_id"`
|
||||
Username string `gorm:"column:username"`
|
||||
|
@ -41,11 +45,12 @@ type OldComment struct {
|
|||
Torrent *Torrent `gorm:"ForeignKey:torrent_id"`
|
||||
}
|
||||
|
||||
// Returns the total size of memory recursively allocated for this struct
|
||||
// Size : Returns the total size of memory recursively allocated for this struct
|
||||
func (c OldComment) Size() int {
|
||||
return (1 + 2*2 + len(c.Username) + len(c.Content) + 3 + 1) * 8
|
||||
}
|
||||
|
||||
// TableName : Return the name of OldComment table
|
||||
func (c OldComment) TableName() string {
|
||||
// cba to rename this in the db
|
||||
// TODO: Update database schema to fix this hack
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/util"
|
||||
)
|
||||
|
||||
// DatabaseDump model
|
||||
type DatabaseDump struct {
|
||||
Date time.Time
|
||||
Filesize int64
|
||||
|
@ -14,6 +15,7 @@ type DatabaseDump struct {
|
|||
TorrentLink string
|
||||
}
|
||||
|
||||
// DatabaseDumpJSON : Json format of DatabaseDump model
|
||||
type DatabaseDumpJSON struct {
|
||||
Date string `json:"date"`
|
||||
Filesize string `json:"filesize"`
|
||||
|
@ -22,6 +24,7 @@ type DatabaseDumpJSON struct {
|
|||
TorrentLink template.URL `json:"torrent"`
|
||||
}
|
||||
|
||||
// ToJSON : convert to JSON DatabaseDump model
|
||||
func (dump *DatabaseDump) ToJSON() DatabaseDumpJSON {
|
||||
json := DatabaseDumpJSON{
|
||||
Date: dump.Date.Format(time.RFC3339),
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/zeebo/bencode"
|
||||
)
|
||||
|
||||
// File model
|
||||
type File struct {
|
||||
ID uint `gorm:"column:file_id;primary_key"`
|
||||
TorrentID uint `gorm:"column:torrent_id;unique_index:idx_tid_path"`
|
||||
|
@ -13,20 +14,23 @@ type File struct {
|
|||
Filesize int64 `gorm:"column:filesize"`
|
||||
}
|
||||
|
||||
// TableName : Return the name of files table
|
||||
func (f File) TableName() string {
|
||||
return config.FilesTableName
|
||||
}
|
||||
|
||||
// Returns the total size of memory allocated for this struct
|
||||
// Size : Returns the total size of memory allocated for this struct
|
||||
func (f File) Size() int {
|
||||
return (2 + len(f.BencodedPath) + 1) * 8
|
||||
}
|
||||
|
||||
// Path : Returns the path to the file
|
||||
func (f *File) Path() (out []string) {
|
||||
bencode.DecodeString(f.BencodedPath, &out)
|
||||
return
|
||||
}
|
||||
|
||||
// SetPath : Set the path of the file
|
||||
func (f *File) SetPath(path []string) error {
|
||||
encoded, err := bencode.EncodeString(path)
|
||||
if err != nil {
|
||||
|
@ -37,6 +41,7 @@ func (f *File) SetPath(path []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Filename : Returns the filename of the file
|
||||
func (f *File) Filename() string {
|
||||
path := f.Path()
|
||||
return path[len(path)-1]
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package model
|
||||
|
||||
// Language model
|
||||
// Is it deprecated?
|
||||
type Language struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
|
|
@ -4,20 +4,23 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/config"
|
||||
)
|
||||
|
||||
// Notification model
|
||||
type Notification struct {
|
||||
ID uint
|
||||
Content string
|
||||
Read bool
|
||||
Identifier string
|
||||
Url string
|
||||
URL string
|
||||
UserID uint
|
||||
// User *User `gorm:"AssociationForeignKey:UserID;ForeignKey:user_id"` // Don't think that we need it here
|
||||
}
|
||||
|
||||
// NewNotification : Create a new notification
|
||||
func NewNotification(identifier string, c string, url string) Notification {
|
||||
return Notification{Identifier: identifier, Content: c, Url: url}
|
||||
return Notification{Identifier: identifier, Content: c, URL: url}
|
||||
}
|
||||
|
||||
// TableName : Return the name of notification table
|
||||
func (n *Notification) TableName() string {
|
||||
return config.NotificationTableName
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/config"
|
||||
)
|
||||
|
||||
// TorrentReport model
|
||||
// User can be null (anonymous reports)
|
||||
// FIXME can't preload field Torrents for model.TorrentReport
|
||||
type TorrentReport struct {
|
||||
|
@ -20,11 +21,13 @@ type TorrentReport struct {
|
|||
User *User `gorm:"AssociationForeignKey:UserID;ForeignKey:user_id"`
|
||||
}
|
||||
|
||||
func (r TorrentReport) TableName() string {
|
||||
// TableName : Return the name of torrent report table
|
||||
func (report TorrentReport) TableName() string {
|
||||
return config.ReportsTableName
|
||||
}
|
||||
|
||||
type TorrentReportJson struct {
|
||||
// TorrentReportJSON : Json struct of torrent report model
|
||||
type TorrentReportJSON struct {
|
||||
ID uint `json:"id"`
|
||||
Description string `json:"description"`
|
||||
Torrent TorrentJSON `json:"torrent"`
|
||||
|
@ -46,23 +49,25 @@ func getReportDescription(d string) string {
|
|||
return "???"
|
||||
}
|
||||
|
||||
func (report *TorrentReport) ToJson() TorrentReportJson {
|
||||
var t TorrentJSON = TorrentJSON{}
|
||||
// ToJSON : conversion to json of a torrent report
|
||||
func (report *TorrentReport) ToJSON() TorrentReportJSON {
|
||||
t := TorrentJSON{}
|
||||
if report.Torrent != nil { // FIXME: report.Torrent should never be nil
|
||||
t = report.Torrent.ToJSON()
|
||||
}
|
||||
var u UserJSON = UserJSON{}
|
||||
u := UserJSON{}
|
||||
if report.User != nil {
|
||||
u = report.User.ToJSON()
|
||||
}
|
||||
json := TorrentReportJson{report.ID, getReportDescription(report.Description), t, u}
|
||||
json := TorrentReportJSON{report.ID, getReportDescription(report.Description), t, u}
|
||||
return json
|
||||
}
|
||||
|
||||
func TorrentReportsToJSON(reports []TorrentReport) []TorrentReportJson {
|
||||
json := make([]TorrentReportJson, len(reports))
|
||||
// TorrentReportsToJSON : Conversion of multiple reports to json
|
||||
func TorrentReportsToJSON(reports []TorrentReport) []TorrentReportJSON {
|
||||
json := make([]TorrentReportJSON, len(reports))
|
||||
for i := range reports {
|
||||
json[i] = reports[i].ToJson()
|
||||
json[i] = reports[i].ToJSON()
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
|
|
@ -16,13 +16,19 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
TorrentStatusNormal = 1
|
||||
TorrentStatusRemake = 2
|
||||
// TorrentStatusNormal Int for Torrent status normal
|
||||
TorrentStatusNormal = 1
|
||||
// TorrentStatusRemake Int for Torrent status remake
|
||||
TorrentStatusRemake = 2
|
||||
// TorrentStatusTrusted Int for Torrent status trusted
|
||||
TorrentStatusTrusted = 3
|
||||
TorrentStatusAPlus = 4
|
||||
// TorrentStatusAPlus Int for Torrent status a+
|
||||
TorrentStatusAPlus = 4
|
||||
// TorrentStatusBlocked Int for Torrent status locked
|
||||
TorrentStatusBlocked = 5
|
||||
)
|
||||
|
||||
// Feed struct
|
||||
type Feed struct {
|
||||
ID int
|
||||
Name string
|
||||
|
@ -31,6 +37,7 @@ type Feed struct {
|
|||
Timestamp string
|
||||
}
|
||||
|
||||
// Torrent model
|
||||
type Torrent struct {
|
||||
ID uint `gorm:"column:torrent_id;primary_key"`
|
||||
Name string `gorm:"column:torrent_name"`
|
||||
|
@ -59,7 +66,7 @@ type Torrent struct {
|
|||
FileList []File `gorm:"ForeignKey:torrent_id"`
|
||||
}
|
||||
|
||||
// Returns the total size of memory recursively allocated for this struct
|
||||
// Size : Returns the total size of memory recursively allocated for this struct
|
||||
// FIXME: doesn't go have sizeof or something nicer for this?
|
||||
func (t Torrent) Size() (s int) {
|
||||
s += 8 + // ints
|
||||
|
@ -85,34 +92,42 @@ func (t Torrent) Size() (s int) {
|
|||
|
||||
}
|
||||
|
||||
// TableName : Return the name of torrents table
|
||||
func (t Torrent) TableName() string {
|
||||
return config.TorrentsTableName
|
||||
}
|
||||
|
||||
// Identifier : Return the identifier of a torrent
|
||||
func (t *Torrent) Identifier() string {
|
||||
return "torrent_" + strconv.Itoa(int(t.ID))
|
||||
}
|
||||
|
||||
// IsNormal : Return if a torrent status is normal
|
||||
func (t Torrent) IsNormal() bool {
|
||||
return t.Status == TorrentStatusNormal
|
||||
}
|
||||
|
||||
// IsRemake : Return if a torrent status is normal
|
||||
func (t Torrent) IsRemake() bool {
|
||||
return t.Status == TorrentStatusRemake
|
||||
}
|
||||
|
||||
// IsTrusted : Return if a torrent status is trusted
|
||||
func (t Torrent) IsTrusted() bool {
|
||||
return t.Status == TorrentStatusTrusted
|
||||
}
|
||||
|
||||
// IsAPlus : Return if a torrent status is a+
|
||||
func (t Torrent) IsAPlus() bool {
|
||||
return t.Status == TorrentStatusAPlus
|
||||
}
|
||||
|
||||
// IsBlocked : Return if a torrent status is locked
|
||||
func (t *Torrent) IsBlocked() bool {
|
||||
return t.Status == TorrentStatusBlocked
|
||||
}
|
||||
|
||||
// IsDeleted : Return if a torrent status is deleted
|
||||
func (t *Torrent) IsDeleted() bool {
|
||||
return t.DeletedAt != nil
|
||||
}
|
||||
|
@ -143,12 +158,14 @@ func (t Torrent) DeleteFromESIndex(client *elastic.Client) error {
|
|||
/* We need a JSON object instead of a Gorm structure because magnet URLs are
|
||||
not in the database and have to be generated dynamically */
|
||||
|
||||
type ApiResultJSON struct {
|
||||
// APIResultJSON for torrents in json for api
|
||||
type APIResultJSON struct {
|
||||
Torrents []TorrentJSON `json:"torrents"`
|
||||
QueryRecordCount int `json:"queryRecordCount"`
|
||||
TotalRecordCount int `json:"totalRecordCount"`
|
||||
}
|
||||
|
||||
// CommentJSON for comment model in json
|
||||
type CommentJSON struct {
|
||||
Username string `json:"username"`
|
||||
UserID int `json:"user_id"`
|
||||
|
@ -157,11 +174,13 @@ type CommentJSON struct {
|
|||
Date time.Time `json:"date"`
|
||||
}
|
||||
|
||||
// FileJSON for file model in json
|
||||
type FileJSON struct {
|
||||
Path string `json:"path"`
|
||||
Filesize int64 `json:"filesize"`
|
||||
}
|
||||
|
||||
// TorrentJSON for torrent model in json for api
|
||||
type TorrentJSON struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
@ -264,7 +283,7 @@ func (t *Torrent) ToJSON() TorrentJSON {
|
|||
|
||||
/* Complete the functions when necessary... */
|
||||
|
||||
// Map Torrents to TorrentsToJSON without reallocations
|
||||
// TorrentsToJSON : Map Torrents to TorrentsToJSON without reallocations
|
||||
func TorrentsToJSON(t []Torrent) []TorrentJSON {
|
||||
json := make([]TorrentJSON, len(t))
|
||||
for i := range t {
|
||||
|
|
|
@ -9,12 +9,17 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
UserStatusBanned = -1
|
||||
UserStatusMember = 0
|
||||
UserStatusTrusted = 1
|
||||
// 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
|
||||
UserStatusModerator = 2
|
||||
)
|
||||
|
||||
// User model
|
||||
type User struct {
|
||||
ID uint `gorm:"column:user_id;primary_key"`
|
||||
Username string `gorm:"column:username"`
|
||||
|
@ -23,8 +28,8 @@ type User struct {
|
|||
Status int `gorm:"column:status"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
ApiToken string `gorm:"column:api_token"`
|
||||
ApiTokenExpiry time.Time `gorm:"column:api_token_expiry"`
|
||||
APIToken string `gorm:"column:api_token"`
|
||||
APITokenExpiry time.Time `gorm:"column:api_token_expiry"`
|
||||
Language string `gorm:"column:language"`
|
||||
UserSettings string `gorm:"column:settings"`
|
||||
|
||||
|
@ -40,6 +45,7 @@ type User struct {
|
|||
Settings UserSettings `gorm:"-"` // We don't want to load settings everytime, stock it as a string, parse it when needed
|
||||
}
|
||||
|
||||
// UserJSON : User model conversion in JSON
|
||||
type UserJSON struct {
|
||||
ID uint `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
|
@ -49,14 +55,14 @@ type UserJSON struct {
|
|||
LikedCount int `json:"liked_count"`
|
||||
}
|
||||
|
||||
// Returns the total size of memory recursively allocated for this struct
|
||||
// Size : Returns the total size of memory recursively allocated for this struct
|
||||
func (u User) Size() (s int) {
|
||||
s += 4 + // ints
|
||||
6*2 + // string pointers
|
||||
4*3 + //time.Time
|
||||
3*2 + // arrays
|
||||
// string arrays
|
||||
len(u.Username) + len(u.Password) + len(u.Email) + len(u.ApiToken) + len(u.MD5) + len(u.Language)
|
||||
len(u.Username) + len(u.Password) + len(u.Email) + len(u.APIToken) + len(u.MD5) + len(u.Language)
|
||||
s *= 8
|
||||
|
||||
// Ignoring foreign key users. Fuck them.
|
||||
|
@ -64,20 +70,28 @@ func (u User) Size() (s int) {
|
|||
return
|
||||
}
|
||||
|
||||
func (u User) IsBanned() bool {
|
||||
// IsBanned : Return true if user is banned
|
||||
func (u *User) IsBanned() bool {
|
||||
return u.Status == UserStatusBanned
|
||||
}
|
||||
func (u User) IsMember() bool {
|
||||
|
||||
// IsMember : Return true if user is member
|
||||
func (u *User) IsMember() bool {
|
||||
return u.Status == UserStatusMember
|
||||
}
|
||||
func (u User) IsTrusted() bool {
|
||||
|
||||
// IsTrusted : Return true if user is tusted
|
||||
func (u *User) IsTrusted() bool {
|
||||
return u.Status == UserStatusTrusted
|
||||
}
|
||||
func (u User) IsModerator() bool {
|
||||
|
||||
// IsModerator : Return true if user is moderator
|
||||
func (u *User) IsModerator() bool {
|
||||
return u.Status == UserStatusModerator
|
||||
}
|
||||
|
||||
func (u User) GetUnreadNotifications() int {
|
||||
// GetUnreadNotifications : Get unread notifications from a user
|
||||
func (u *User) GetUnreadNotifications() int {
|
||||
if u.UnreadNotifications == 0 {
|
||||
for _, notif := range u.Notifications {
|
||||
if !notif.Read {
|
||||
|
@ -88,30 +102,35 @@ func (u User) GetUnreadNotifications() int {
|
|||
return u.UnreadNotifications
|
||||
}
|
||||
|
||||
// PublicUser : Is it Deprecated?
|
||||
type PublicUser struct {
|
||||
User *User
|
||||
}
|
||||
|
||||
// different users following eachother
|
||||
// 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"`
|
||||
TorrentID uint `gorm:"column:torrent_id"`
|
||||
}
|
||||
|
||||
// UserSettings : Struct for user settings, not a model
|
||||
type UserSettings struct {
|
||||
Settings map[string]bool `json:"settings"`
|
||||
}
|
||||
|
||||
// TableName : Return the name of OldComment table
|
||||
func (c UserUploadsOld) TableName() string {
|
||||
// is this needed here?
|
||||
return config.UploadsOldTableName
|
||||
}
|
||||
|
||||
// ToJSON : Conversion of a user model to json
|
||||
func (u *User) ToJSON() UserJSON {
|
||||
json := UserJSON{
|
||||
ID: u.ID,
|
||||
|
@ -126,18 +145,20 @@ func (u *User) ToJSON() UserJSON {
|
|||
|
||||
/* User Settings */
|
||||
|
||||
// Get a user setting by keyname
|
||||
func (s *UserSettings) Get(key string) bool {
|
||||
if val, ok := s.Settings[key]; ok {
|
||||
return val
|
||||
} else {
|
||||
return config.DefaultUserSettings[key]
|
||||
}
|
||||
return config.DefaultUserSettings[key]
|
||||
}
|
||||
|
||||
// GetSettings : get all user settings
|
||||
func (s *UserSettings) GetSettings() map[string]bool {
|
||||
return s.Settings
|
||||
}
|
||||
|
||||
// Set a user setting by keyname
|
||||
func (s *UserSettings) Set(key string, val bool) {
|
||||
if s.Settings == nil {
|
||||
s.Settings = make(map[string]bool)
|
||||
|
@ -145,14 +166,16 @@ func (s *UserSettings) Set(key string, val bool) {
|
|||
s.Settings[key] = val
|
||||
}
|
||||
|
||||
// ToDefault : Set user settings to default
|
||||
func (s *UserSettings) ToDefault() {
|
||||
s.Settings = config.DefaultUserSettings
|
||||
}
|
||||
|
||||
func (s *UserSettings) Initialize() {
|
||||
func (s *UserSettings) initialize() {
|
||||
s.Settings = make(map[string]bool)
|
||||
}
|
||||
|
||||
// SaveSettings : Format settings into a json string for preparing before user insertion
|
||||
func (u *User) SaveSettings() {
|
||||
byteArray, err := json.Marshal(u.Settings)
|
||||
|
||||
|
@ -162,12 +185,13 @@ func (u *User) SaveSettings() {
|
|||
u.UserSettings = string(byteArray)
|
||||
}
|
||||
|
||||
// ParseSettings : Function to parse json string into usersettings struct, only parse if necessary
|
||||
func (u *User) ParseSettings() {
|
||||
if len(u.Settings.GetSettings()) == 0 && u.UserSettings != "" {
|
||||
u.Settings.Initialize()
|
||||
u.Settings.initialize()
|
||||
json.Unmarshal([]byte(u.UserSettings), &u.Settings)
|
||||
} else if len(u.Settings.GetSettings()) == 0 && u.UserSettings != "" {
|
||||
u.Settings.Initialize()
|
||||
u.Settings.initialize()
|
||||
u.Settings.ToDefault()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
// implements io.Closer that gracefully closes an http server
|
||||
// GracefulHttpCloser : implements io.Closer that gracefully closes an http server
|
||||
type GracefulHttpCloser struct {
|
||||
Server *http.Server
|
||||
Listener net.Listener
|
||||
}
|
||||
|
||||
// Close method
|
||||
func (c *GracefulHttpCloser) Close() error {
|
||||
c.Listener.Close()
|
||||
return c.Server.Shutdown(nil)
|
||||
|
|
|
@ -13,6 +13,7 @@ type GracefulListener struct {
|
|||
stop chan int
|
||||
}
|
||||
|
||||
// Accept method
|
||||
func (l *GracefulListener) Accept() (net.Conn, error) {
|
||||
for {
|
||||
c, err := l.listener.Accept()
|
||||
|
@ -37,6 +38,7 @@ func (l *GracefulListener) Accept() (net.Conn, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Close method
|
||||
func (l *GracefulListener) Close() (err error) {
|
||||
l.listener.Close()
|
||||
if l.stop != nil {
|
||||
|
@ -45,6 +47,7 @@ func (l *GracefulListener) Close() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Addr method
|
||||
func (l *GracefulListener) Addr() net.Addr {
|
||||
return l.listener.Addr()
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ func APIHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
b := model.ApiResultJSON{
|
||||
b := model.APIResultJSON{
|
||||
Torrents: model.TorrentsToJSON(torrents),
|
||||
}
|
||||
b.QueryRecordCount = req.MaxPerPage
|
||||
|
@ -95,7 +95,7 @@ func APIViewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
torrent, err := torrentService.GetTorrentById(id)
|
||||
torrent, err := torrentService.GetTorrentByID(id)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
|
@ -120,7 +120,7 @@ func APIViewHeadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
_, err = torrentService.GetRawTorrentById(uint(id))
|
||||
_, err = torrentService.GetRawTorrentByID(uint(id))
|
||||
|
||||
if err != nil {
|
||||
NotFoundHandler(w, r)
|
||||
|
@ -142,7 +142,7 @@ func APIUploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if user.ID == 0 {
|
||||
http.Error(w, apiService.ErrApiKey.Error(), http.StatusUnauthorized)
|
||||
http.Error(w, apiService.ErrAPIKey.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,7 @@ func APIUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
contentType := r.Header.Get("Content-Type")
|
||||
if contentType == "application/json" {
|
||||
if user.ID == 0 {
|
||||
http.Error(w, apiService.ErrApiKey.Error(), http.StatusForbidden)
|
||||
http.Error(w, apiService.ErrAPIKey.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,7 @@ func APIUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
torrent := model.Torrent{}
|
||||
db.ORM.Where("torrent_id = ?", id).First(&torrent)
|
||||
if torrent.ID == 0 {
|
||||
http.Error(w, apiService.ErrTorrentId.Error(), http.StatusBadRequest)
|
||||
http.Error(w, apiService.ErrTorrentID.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if torrent.UploaderID != 0 && torrent.UploaderID != user.ID { //&& user.Status != mod
|
||||
|
|
|
@ -91,7 +91,7 @@ func (f *ReassignForm) ExecuteAction() (int, error) {
|
|||
|
||||
num := 0
|
||||
for _, torrentID := range toBeChanged {
|
||||
torrent, err2 := torrentService.GetRawTorrentById(torrentID)
|
||||
torrent, err2 := torrentService.GetRawTorrentByID(torrentID)
|
||||
if err2 == nil {
|
||||
torrent.UploaderID = f.AssignTo
|
||||
db.ORM.Model(&torrent).UpdateColumn(&torrent)
|
||||
|
@ -261,7 +261,7 @@ func CommentsListPanel(w http.ResponseWriter, r *http.Request) {
|
|||
// TorrentEditModPanel : Controller for editing a torrent after GET request
|
||||
func TorrentEditModPanel(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get("id")
|
||||
torrent, _ := torrentService.GetTorrentById(id)
|
||||
torrent, _ := torrentService.GetTorrentByID(id)
|
||||
messages := msg.GetMessages(r)
|
||||
|
||||
torrentJSON := torrent.ToJSON()
|
||||
|
@ -281,7 +281,7 @@ func TorrentPostEditModPanel(w http.ResponseWriter, r *http.Request) {
|
|||
var uploadForm uploadForm
|
||||
id := r.URL.Query().Get("id")
|
||||
messages := msg.GetMessages(r)
|
||||
torrent, _ := torrentService.GetTorrentById(id)
|
||||
torrent, _ := torrentService.GetTorrentByID(id)
|
||||
if torrent.ID > 0 {
|
||||
errUp := uploadForm.ExtractEditInfo(r)
|
||||
if errUp != nil {
|
||||
|
@ -309,7 +309,7 @@ func TorrentPostEditModPanel(w http.ResponseWriter, r *http.Request) {
|
|||
func CommentDeleteModPanel(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get("id")
|
||||
|
||||
_, _ = userService.DeleteComment(id)
|
||||
_, _ = commentService.DeleteComment(id)
|
||||
url, _ := Router.Get("mod_clist").URL()
|
||||
http.Redirect(w, r, url.String()+"?deleted", http.StatusSeeOther)
|
||||
}
|
||||
|
@ -567,7 +567,7 @@ func torrentManyAction(r *http.Request) {
|
|||
|
||||
if !messages.HasErrors() {
|
||||
for _, torrentID := range torrentsSelected {
|
||||
torrent, _ := torrentService.GetTorrentById(torrentID)
|
||||
torrent, _ := torrentService.GetTorrentByID(torrentID)
|
||||
if torrent.ID > 0 && userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) {
|
||||
if action == "status" || action == "multiple" || action == "category" || action == "owner" {
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ func RSSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
for i, torrent := range torrents {
|
||||
torrentJSON := torrent.ToJSON()
|
||||
feed.Items[i] = &feeds.Item{
|
||||
Id: "https://" + config.WebAddress + "/view/" + torrentJSON.ID,
|
||||
ID: "https://" + config.WebAddress + "/view/" + torrentJSON.ID,
|
||||
Title: torrent.Name,
|
||||
Link: &feeds.Link{Href: string(torrentJSON.Magnet)},
|
||||
Description: string(torrentJSON.Description),
|
||||
|
|
|
@ -84,7 +84,7 @@ type changeLanguageVariables struct {
|
|||
type panelIndexVbs struct {
|
||||
commonTemplateVariables
|
||||
Torrents []model.Torrent
|
||||
TorrentReports []model.TorrentReportJson
|
||||
TorrentReports []model.TorrentReportJSON
|
||||
Users []model.User
|
||||
Comments []model.Comment
|
||||
}
|
||||
|
|
|
@ -343,8 +343,8 @@ func UserAPIKeyResetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
NotFoundHandler(w, r)
|
||||
return
|
||||
}
|
||||
userProfile.ApiToken, _ = crypto.GenerateRandomToken32()
|
||||
userProfile.ApiTokenExpiry = time.Unix(0, 0)
|
||||
userProfile.APIToken, _ = crypto.GenerateRandomToken32()
|
||||
userProfile.APITokenExpiry = time.Unix(0, 0)
|
||||
_, errorUser = userService.UpdateUserCore(&userProfile)
|
||||
if errorUser != nil {
|
||||
messages.ImportFromError("errors", errorUser)
|
||||
|
|
|
@ -34,7 +34,7 @@ func ViewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
messages.AddInfo("infos", "Torrent uploaded successfully!")
|
||||
}
|
||||
|
||||
torrent, err := torrentService.GetTorrentById(id)
|
||||
torrent, err := torrentService.GetTorrentByID(id)
|
||||
|
||||
if r.URL.Query()["notif"] != nil {
|
||||
notifierService.ToggleReadNotification(torrent.Identifier(), user.ID)
|
||||
|
@ -66,7 +66,7 @@ func ViewHeadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
_, err = torrentService.GetRawTorrentById(uint(id))
|
||||
_, err = torrentService.GetRawTorrentByID(uint(id))
|
||||
|
||||
if err != nil {
|
||||
NotFoundHandler(w, r)
|
||||
|
@ -81,7 +81,7 @@ func PostCommentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
torrent, err := torrentService.GetTorrentById(id)
|
||||
torrent, err := torrentService.GetTorrentByID(id)
|
||||
if err != nil {
|
||||
NotFoundHandler(w, r)
|
||||
return
|
||||
|
@ -155,7 +155,7 @@ func ReportTorrentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// TorrentEditUserPanel : Controller for editing a user torrent by a user, after GET request
|
||||
func TorrentEditUserPanel(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get("id")
|
||||
torrent, _ := torrentService.GetTorrentById(id)
|
||||
torrent, _ := torrentService.GetTorrentByID(id)
|
||||
messages := msg.GetMessages(r)
|
||||
currentUser := getUser(r)
|
||||
if userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) {
|
||||
|
@ -178,7 +178,7 @@ func TorrentPostEditUserPanel(w http.ResponseWriter, r *http.Request) {
|
|||
var uploadForm uploadForm
|
||||
id := r.URL.Query().Get("id")
|
||||
messages := msg.GetMessages(r)
|
||||
torrent, _ := torrentService.GetTorrentById(id)
|
||||
torrent, _ := torrentService.GetTorrentByID(id)
|
||||
currentUser := getUser(r)
|
||||
if torrent.ID > 0 && userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) {
|
||||
errUp := uploadForm.ExtractEditInfo(r)
|
||||
|
@ -216,7 +216,7 @@ func TorrentPostEditUserPanel(w http.ResponseWriter, r *http.Request) {
|
|||
func TorrentDeleteUserPanel(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get("id")
|
||||
currentUser := getUser(r)
|
||||
torrent, _ := torrentService.GetTorrentById(id)
|
||||
torrent, _ := torrentService.GetTorrentByID(id)
|
||||
if userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) {
|
||||
_, err := torrentService.DeleteTorrent(id)
|
||||
if err == nil {
|
||||
|
|
|
@ -27,12 +27,14 @@ type torrentsQuery struct {
|
|||
Downloads int `json:"downloads"`
|
||||
}
|
||||
|
||||
// TorrentsRequest struct
|
||||
type TorrentsRequest struct {
|
||||
Query torrentsQuery `json:"search"`
|
||||
Page int `json:"page"`
|
||||
MaxPerPage int `json:"limit"`
|
||||
}
|
||||
|
||||
// TorrentRequest struct
|
||||
//accept torrent files?
|
||||
type TorrentRequest struct {
|
||||
Name string `json:"name"`
|
||||
|
@ -43,11 +45,13 @@ type TorrentRequest struct {
|
|||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// UpdateRequest struct
|
||||
type UpdateRequest struct {
|
||||
ID int `json:"id"`
|
||||
Update TorrentRequest `json:"update"`
|
||||
}
|
||||
|
||||
// ToParams : Convert a torrentsrequest to searchparams
|
||||
func (r *TorrentsRequest) ToParams() serviceBase.WhereParams {
|
||||
res := serviceBase.WhereParams{}
|
||||
conditions := ""
|
||||
|
@ -91,11 +95,11 @@ func validateSubCategory(r *TorrentRequest) (error, int) {
|
|||
}
|
||||
|
||||
func validateMagnet(r *TorrentRequest) (error, int) {
|
||||
magnetUrl, err := url.Parse(string(r.Magnet)) //?
|
||||
magnetURL, err := url.Parse(string(r.Magnet)) //?
|
||||
if err != nil {
|
||||
return err, http.StatusInternalServerError
|
||||
}
|
||||
xt := magnetUrl.Query().Get("xt")
|
||||
xt := magnetURL.Query().Get("xt")
|
||||
if !strings.HasPrefix(xt, "urn:btih:") {
|
||||
return ErrMagnet, http.StatusNotAcceptable
|
||||
}
|
||||
|
@ -132,6 +136,7 @@ func validateHash(r *TorrentRequest) (error, int) {
|
|||
return nil, http.StatusOK
|
||||
}
|
||||
|
||||
// ValidateUpload : Check if an upload is valid
|
||||
func (r *TorrentRequest) ValidateUpload() (err error, code int) {
|
||||
validators := []func(r *TorrentRequest) (error, int){
|
||||
validateName,
|
||||
|
@ -153,6 +158,7 @@ func (r *TorrentRequest) ValidateUpload() (err error, code int) {
|
|||
return err, code
|
||||
}
|
||||
|
||||
// ValidateMultipartUpload : Check if multipart upload is valid
|
||||
func (r *TorrentRequest) ValidateMultipartUpload(req *http.Request) (int64, error, int) {
|
||||
tfile, _, err := req.FormFile("torrent")
|
||||
if err == nil {
|
||||
|
@ -191,6 +197,7 @@ func (r *TorrentRequest) ValidateMultipartUpload(req *http.Request) (int64, erro
|
|||
return 0, err, http.StatusInternalServerError
|
||||
}
|
||||
|
||||
// ValidateUpdate : Check if an update is valid
|
||||
func (r *TorrentRequest) ValidateUpdate() (err error, code int) {
|
||||
validators := []func(r *TorrentRequest) (error, int){
|
||||
validateName,
|
||||
|
@ -217,6 +224,7 @@ func (r *TorrentRequest) ValidateUpdate() (err error, code int) {
|
|||
return err, code
|
||||
}
|
||||
|
||||
// UpdateTorrent : Update torrent model
|
||||
//rewrite with reflect ?
|
||||
func (r *UpdateRequest) UpdateTorrent(t *model.Torrent) {
|
||||
if r.Update.Name != "" {
|
||||
|
|
|
@ -2,11 +2,26 @@ package apiService
|
|||
|
||||
import "errors"
|
||||
|
||||
// ErrShortName : Error for invalid file name used by api
|
||||
var ErrShortName = errors.New("file name should be at least 100 characters long")
|
||||
|
||||
// ErrCategory : Error for not found category used by api
|
||||
var ErrCategory = errors.New("this category doesn't exist")
|
||||
|
||||
// ErrSubCategory : Error for not found sub category used by api
|
||||
var ErrSubCategory = errors.New("this sub category doesn't exist")
|
||||
|
||||
// ErrMagnet : Error for incorrect magnet used by api
|
||||
var ErrMagnet = errors.New("incorrect magnet")
|
||||
|
||||
// ErrHash : Error for incorrect hash used by api
|
||||
var ErrHash = errors.New("incorrect hash")
|
||||
var ErrApiKey = errors.New("incorrect api key")
|
||||
var ErrTorrentId = errors.New("torrent with requested id doesn't exist")
|
||||
|
||||
// ErrAPIKey : Error for incorrect api key used by api
|
||||
var ErrAPIKey = errors.New("incorrect api key")
|
||||
|
||||
// ErrTorrentID : Error for torrent id used by api
|
||||
var ErrTorrentID = errors.New("torrent with requested id doesn't exist")
|
||||
|
||||
// ErrRights : Error for rights used by api
|
||||
var ErrRights = errors.New("not enough rights for this request")
|
||||
|
|
|
@ -11,7 +11,8 @@ import (
|
|||
const lifetime = time.Minute * 20
|
||||
|
||||
var (
|
||||
server = captcha.Server(captcha.StdWidth, captcha.StdHeight)
|
||||
server = captcha.Server(captcha.StdWidth, captcha.StdHeight)
|
||||
// ErrInvalidCaptcha : Error when captcha is invalid
|
||||
ErrInvalidCaptcha = errors.New("invalid captcha")
|
||||
)
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package commentService
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/NyaaPantsu/nyaa/db"
|
||||
"github.com/NyaaPantsu/nyaa/model"
|
||||
)
|
||||
|
||||
// GetAllComments : Get all comments based on conditions
|
||||
func GetAllComments(limit int, offset int, conditions string, values ...interface{}) ([]model.Comment, int) {
|
||||
var comments []model.Comment
|
||||
var nbComments int
|
||||
|
@ -12,3 +16,16 @@ func GetAllComments(limit int, offset int, conditions string, values ...interfac
|
|||
db.ORM.Preload("User").Limit(limit).Offset(offset).Where(conditions, values...).Find(&comments)
|
||||
return comments, nbComments
|
||||
}
|
||||
|
||||
// DeleteComment : Delete a comment
|
||||
// FIXME : move this to comment service
|
||||
func DeleteComment(id string) (int, error) {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/model"
|
||||
)
|
||||
|
||||
// NotifyUser : Notify a user with a notification according to his settings
|
||||
func NotifyUser(user *model.User, name string, msg string, url string, email bool) {
|
||||
if user.ID > 0 {
|
||||
notification := model.NewNotification(name, msg, url)
|
||||
|
@ -17,10 +18,12 @@ func NotifyUser(user *model.User, name string, msg string, url string, email boo
|
|||
}
|
||||
}
|
||||
|
||||
// ToggleReadNotification : Make a notification as read according to its identifier
|
||||
func ToggleReadNotification(identifier string, id uint) { //
|
||||
db.ORM.Model(&model.Notification{}).Where("identifier = ? AND user_id = ?", identifier, id).Updates(model.Notification{Read: true})
|
||||
}
|
||||
|
||||
// DeleteAllNotifications : Erase notifications from a user
|
||||
func DeleteAllNotifications(id uint) { //
|
||||
db.ORM.Where("user_id = ?", id).Delete(&model.Notification{})
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/service"
|
||||
)
|
||||
|
||||
// Return torrentReport in case we did modified it (ie: CreatedAt field)
|
||||
// CreateTorrentReport : Return torrentReport in case we did modified it (ie: CreatedAt field)
|
||||
func CreateTorrentReport(torrentReport model.TorrentReport) error {
|
||||
if db.ORM.Create(&torrentReport).Error != nil {
|
||||
return errors.New("TorrentReport was not created")
|
||||
|
@ -19,10 +19,11 @@ func CreateTorrentReport(torrentReport model.TorrentReport) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeleteTorrentReport : Delete a torrent report by id
|
||||
func DeleteTorrentReport(id uint) (error, int) {
|
||||
var torrentReport model.TorrentReport
|
||||
if db.ORM.First(&torrentReport, id).RecordNotFound() {
|
||||
return errors.New("Trying to delete a torrent report that does not exists."), http.StatusNotFound
|
||||
return errors.New("Trying to delete a torrent report that does not exists"), http.StatusNotFound
|
||||
}
|
||||
if err := db.ORM.Delete(&torrentReport).Error; err != nil {
|
||||
return err, http.StatusInternalServerError
|
||||
|
@ -30,10 +31,11 @@ func DeleteTorrentReport(id uint) (error, int) {
|
|||
return nil, http.StatusOK
|
||||
}
|
||||
|
||||
// DeleteDefinitelyTorrentReport : Delete definitely a torrent report by id
|
||||
func DeleteDefinitelyTorrentReport(id uint) (error, int) {
|
||||
var torrentReport model.TorrentReport
|
||||
if db.ORM.Unscoped().First(&torrentReport, id).RecordNotFound() {
|
||||
return errors.New("Trying to delete a torrent report that does not exists."), http.StatusNotFound
|
||||
return errors.New("Trying to delete a torrent report that does not exists"), http.StatusNotFound
|
||||
}
|
||||
if err := db.ORM.Unscoped().Delete(&torrentReport).Error; err != nil {
|
||||
return err, http.StatusInternalServerError
|
||||
|
@ -77,10 +79,12 @@ func getTorrentReportsOrderBy(parameters *serviceBase.WhereParams, orderBy strin
|
|||
return
|
||||
}
|
||||
|
||||
// GetTorrentReportsOrderBy : Get torrents based on search parameters with order
|
||||
func GetTorrentReportsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) ([]model.TorrentReport, int, error) {
|
||||
return getTorrentReportsOrderBy(parameters, orderBy, limit, offset, true)
|
||||
}
|
||||
|
||||
// GetAllTorrentReports : Get all torrents
|
||||
func GetAllTorrentReports(limit int, offset int) ([]model.TorrentReport, int, error) {
|
||||
return GetTorrentReportsOrderBy(nil, "", limit, offset)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package serviceBase
|
||||
|
||||
// WhereParams struct for search
|
||||
type WhereParams struct {
|
||||
Conditions string // Ex : name LIKE ? AND category_id LIKE ?
|
||||
Params []interface{}
|
||||
}
|
||||
|
||||
// CreateWhereParams : function to create WhereParams struct for search
|
||||
func CreateWhereParams(conditions string, params ...interface{}) WhereParams {
|
||||
whereParams := WhereParams{
|
||||
Conditions: conditions,
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
package torrentform
|
||||
|
||||
type PanelPost struct {
|
||||
Name string `form:"name" needed:"true" len_min:"3" len_max:"20"`
|
||||
Hash string `form:"hash" needed:"true"`
|
||||
Category int `form:"cat" needed:"true"`
|
||||
Sub_Category int `form:"subcat"`
|
||||
Status string `form:"status" needed:"true"`
|
||||
Description string `form:"desc"`
|
||||
WebsiteLink string `form:"website"`
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
package metainfoFetcher
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/NyaaPantsu/nyaa/config"
|
||||
"github.com/NyaaPantsu/nyaa/db"
|
||||
"github.com/NyaaPantsu/nyaa/model"
|
||||
|
@ -10,11 +14,9 @@ import (
|
|||
"github.com/anacrolix/torrent"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"golang.org/x/time/rate"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MetainfoFetcher Struct
|
||||
type MetainfoFetcher struct {
|
||||
torrentClient *torrent.Client
|
||||
results chan Result
|
||||
|
@ -34,6 +36,7 @@ type MetainfoFetcher struct {
|
|||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// New : Creates a MetainfoFetcher struct
|
||||
func New(fetcherConfig *config.MetainfoFetcherConfig) (fetcher *MetainfoFetcher, err error) {
|
||||
clientConfig := torrent.Config{}
|
||||
// Well, it seems this is the right way to convert speed -> rate.Limiter
|
||||
|
@ -306,12 +309,14 @@ func (fetcher *MetainfoFetcher) run() {
|
|||
}
|
||||
}
|
||||
|
||||
// RunAsync method
|
||||
func (fetcher *MetainfoFetcher) RunAsync() {
|
||||
fetcher.wg.Add(1)
|
||||
|
||||
go fetcher.run()
|
||||
}
|
||||
|
||||
// Close method
|
||||
func (fetcher *MetainfoFetcher) Close() error {
|
||||
fetcher.queueMutex.Lock()
|
||||
defer fetcher.queueMutex.Unlock()
|
||||
|
@ -328,6 +333,7 @@ func (fetcher *MetainfoFetcher) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Wait method
|
||||
func (fetcher *MetainfoFetcher) Wait() {
|
||||
fetcher.wg.Wait()
|
||||
}
|
||||
|
|
|
@ -2,26 +2,30 @@ package metainfoFetcher
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/NyaaPantsu/nyaa/config"
|
||||
"github.com/NyaaPantsu/nyaa/model"
|
||||
"github.com/NyaaPantsu/nyaa/util"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FetchOperation struct
|
||||
type FetchOperation struct {
|
||||
fetcher *MetainfoFetcher
|
||||
torrent model.Torrent
|
||||
done chan int
|
||||
}
|
||||
|
||||
// Result struct
|
||||
type Result struct {
|
||||
operation *FetchOperation
|
||||
err error
|
||||
info *metainfo.Info
|
||||
}
|
||||
|
||||
// NewFetchOperation : Creates a new fetchoperation
|
||||
func NewFetchOperation(fetcher *MetainfoFetcher, dbEntry model.Torrent) (op *FetchOperation) {
|
||||
op = &FetchOperation{
|
||||
fetcher: fetcher,
|
||||
|
@ -31,7 +35,7 @@ func NewFetchOperation(fetcher *MetainfoFetcher, dbEntry model.Torrent) (op *Fet
|
|||
return
|
||||
}
|
||||
|
||||
// Should be started from a goroutine somewhere
|
||||
// Start : Should be started from a goroutine somewhere
|
||||
func (op *FetchOperation) Start(out chan Result) {
|
||||
defer op.fetcher.wg.Done()
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
*
|
||||
*/
|
||||
|
||||
// don't need raw SQL once we get MySQL
|
||||
// GetFeeds : don't need raw SQL once we get MySQL
|
||||
func GetFeeds() (result []model.Feed, err error) {
|
||||
result = make([]model.Feed, 0, 50)
|
||||
rows, err := db.ORM.DB().
|
||||
|
@ -48,18 +48,19 @@ func GetFeeds() (result []model.Feed, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func GetTorrentById(id string) (torrent model.Torrent, err error) {
|
||||
// GetTorrentByID : get a torrent with its id
|
||||
func GetTorrentByID(id string) (torrent model.Torrent, err error) {
|
||||
// Postgres DB integer size is 32-bit
|
||||
id_int, err := strconv.ParseInt(id, 10, 32)
|
||||
idInt, err := strconv.ParseInt(id, 10, 32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tmp := db.ORM.Where("torrent_id = ?", id).Preload("Comments")
|
||||
if id_int > config.LastOldTorrentID {
|
||||
if idInt > config.LastOldTorrentID {
|
||||
tmp = tmp.Preload("FileList")
|
||||
}
|
||||
if id_int <= config.LastOldTorrentID && !config.IsSukebei() {
|
||||
if idInt <= config.LastOldTorrentID && !config.IsSukebei() {
|
||||
// only preload old comments if they could actually exist
|
||||
tmp = tmp.Preload("OldComments")
|
||||
}
|
||||
|
@ -68,7 +69,7 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
|
|||
return
|
||||
}
|
||||
if tmp.Find(&torrent).RecordNotFound() {
|
||||
err = errors.New("Article is not found.")
|
||||
err = errors.New("Article is not found")
|
||||
return
|
||||
}
|
||||
// GORM relly likes not doing its job correctly
|
||||
|
@ -93,25 +94,29 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// GetRawTorrentByID : Get torrent with id without user or comments
|
||||
// won't fetch user or comments
|
||||
func GetRawTorrentById(id uint) (torrent model.Torrent, err error) {
|
||||
func GetRawTorrentByID(id uint) (torrent model.Torrent, err error) {
|
||||
err = nil
|
||||
if db.ORM.Where("torrent_id = ?", id).Find(&torrent).RecordNotFound() {
|
||||
err = errors.New("Article is not found.")
|
||||
err = errors.New("Article is not found")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetTorrentsOrderByNoCount : Get torrents based on search without counting and user
|
||||
func GetTorrentsOrderByNoCount(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, err error) {
|
||||
torrents, _, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, false, false, false)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTorrentsOrderBy : Get torrents based on search without user
|
||||
func GetTorrentsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, count int, err error) {
|
||||
torrents, count, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, true, false, false)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTorrentsWithUserOrderBy : Get torrents based on search with user
|
||||
func GetTorrentsWithUserOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, count int, err error) {
|
||||
torrents, count, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, true, true, false)
|
||||
return
|
||||
|
@ -179,30 +184,34 @@ func GetTorrents(parameters serviceBase.WhereParams, limit int, offset int) ([]m
|
|||
return GetTorrentsOrderBy(¶meters, "", limit, offset)
|
||||
}
|
||||
|
||||
// Get Torrents with where parameters but no limit and order by default (get all the torrents corresponding in the db)
|
||||
// GetTorrentsDB : Get Torrents with where parameters but no limit and order by default (get all the torrents corresponding in the db)
|
||||
func GetTorrentsDB(parameters serviceBase.WhereParams) ([]model.Torrent, int, error) {
|
||||
return GetTorrentsOrderBy(¶meters, "", 0, 0)
|
||||
}
|
||||
|
||||
// GetAllTorrentsOrderBy : Get all torrents ordered by parameters
|
||||
func GetAllTorrentsOrderBy(orderBy string, limit int, offset int) ([]model.Torrent, int, error) {
|
||||
return GetTorrentsOrderBy(nil, orderBy, limit, offset)
|
||||
}
|
||||
|
||||
// GetAllTorrents : Get all torrents without order
|
||||
func GetAllTorrents(limit int, offset int) ([]model.Torrent, int, error) {
|
||||
return GetTorrentsOrderBy(nil, "", limit, offset)
|
||||
}
|
||||
|
||||
// GetAllTorrentsDB : Get all torrents
|
||||
func GetAllTorrentsDB() ([]model.Torrent, int, error) {
|
||||
return GetTorrentsOrderBy(nil, "", 0, 0)
|
||||
}
|
||||
|
||||
// DeleteTorrent : delete a torrent based on id
|
||||
func DeleteTorrent(id string) (int, error) {
|
||||
var torrent model.Torrent
|
||||
if db.ORM.First(&torrent, id).RecordNotFound() {
|
||||
return http.StatusNotFound, errors.New("Torrent is not found.")
|
||||
return http.StatusNotFound, errors.New("Torrent is not found")
|
||||
}
|
||||
if db.ORM.Delete(&torrent).Error != nil {
|
||||
return http.StatusInternalServerError, errors.New("Torrent was not deleted.")
|
||||
return http.StatusInternalServerError, errors.New("Torrent was not deleted")
|
||||
}
|
||||
|
||||
// TODO Don't create a new client for each request
|
||||
|
@ -220,13 +229,14 @@ func DeleteTorrent(id string) (int, error) {
|
|||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
// DefinitelyDeleteTorrent : deletes definitely a torrent based on id
|
||||
func DefinitelyDeleteTorrent(id string) (int, error) {
|
||||
var torrent model.Torrent
|
||||
if db.ORM.Unscoped().Model(&torrent).First(&torrent, id).RecordNotFound() {
|
||||
return http.StatusNotFound, errors.New("Torrent is not found.")
|
||||
return http.StatusNotFound, errors.New("Torrent is not found")
|
||||
}
|
||||
if db.ORM.Unscoped().Model(&torrent).Delete(&torrent).Error != nil {
|
||||
return http.StatusInternalServerError, errors.New("Torrent was not deleted.")
|
||||
return http.StatusInternalServerError, errors.New("Torrent was not deleted")
|
||||
}
|
||||
|
||||
// TODO Don't create a new client for each request
|
||||
|
@ -244,10 +254,11 @@ func DefinitelyDeleteTorrent(id string) (int, error) {
|
|||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
// ToggleBlockTorrent ; Lock/Unlock a torrent based on id
|
||||
func ToggleBlockTorrent(id string) (model.Torrent, int, error) {
|
||||
var torrent model.Torrent
|
||||
if db.ORM.Unscoped().Model(&torrent).First(&torrent, id).RecordNotFound() {
|
||||
return torrent, http.StatusNotFound, errors.New("Torrent is not found.")
|
||||
return torrent, http.StatusNotFound, errors.New("Torrent is not found")
|
||||
}
|
||||
if torrent.Status == model.TorrentStatusBlocked {
|
||||
torrent.Status = model.TorrentStatusNormal
|
||||
|
@ -255,14 +266,15 @@ func ToggleBlockTorrent(id string) (model.Torrent, int, error) {
|
|||
torrent.Status = model.TorrentStatusBlocked
|
||||
}
|
||||
if db.ORM.Unscoped().Model(&torrent).UpdateColumn(&torrent).Error != nil {
|
||||
return torrent, http.StatusInternalServerError, errors.New("Torrent was not updated.")
|
||||
return torrent, http.StatusInternalServerError, errors.New("Torrent was not updated")
|
||||
}
|
||||
return torrent, http.StatusOK, nil
|
||||
}
|
||||
|
||||
// UpdateTorrent : Update a torrent based on model
|
||||
func UpdateTorrent(torrent model.Torrent) (int, error) {
|
||||
if db.ORM.Model(&torrent).UpdateColumn(&torrent).Error != nil {
|
||||
return http.StatusInternalServerError, errors.New("Torrent was not updated.")
|
||||
return http.StatusInternalServerError, errors.New("Torrent was not updated")
|
||||
}
|
||||
|
||||
// TODO Don't create a new client for each request
|
||||
|
@ -281,6 +293,7 @@ func UpdateTorrent(torrent model.Torrent) (int, error) {
|
|||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
// GetDeletedTorrents : Gets deleted torrents based on search params
|
||||
func GetDeletedTorrents(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, count int, err error) {
|
||||
torrents, count, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, true, true, true)
|
||||
return
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/model"
|
||||
)
|
||||
|
||||
// CheckTrackers : Check if there is good trackers in torrent
|
||||
func CheckTrackers(trackers []string) bool {
|
||||
// TODO: move to runtime configuration
|
||||
var deadTrackers = []string{ // substring matches!
|
||||
|
@ -40,6 +41,7 @@ func CheckTrackers(trackers []string) bool {
|
|||
return numGood > 0
|
||||
}
|
||||
|
||||
// IsUploadEnabled : Check if upload is enabled in config
|
||||
func IsUploadEnabled(u model.User) bool {
|
||||
if config.UploadsDisabled {
|
||||
if config.AdminsAreStillAllowedTo && u.IsModerator() {
|
||||
|
|
|
@ -2,6 +2,10 @@ package userService
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/NyaaPantsu/nyaa/db"
|
||||
"github.com/NyaaPantsu/nyaa/model"
|
||||
formStruct "github.com/NyaaPantsu/nyaa/service/user/form"
|
||||
|
@ -11,13 +15,12 @@ import (
|
|||
"github.com/gorilla/context"
|
||||
"github.com/gorilla/securecookie"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
CookieName = "session"
|
||||
// CookieName : Name of cookie
|
||||
CookieName = "session"
|
||||
// UserContextKey : key for user context
|
||||
UserContextKey = "user"
|
||||
)
|
||||
|
||||
|
@ -26,30 +29,32 @@ var cookieHandler = securecookie.New(
|
|||
securecookie.GenerateRandomKey(64),
|
||||
securecookie.GenerateRandomKey(32))
|
||||
|
||||
// Encoding & Decoding of the cookie value
|
||||
func DecodeCookie(cookie_value string) (uint, error) {
|
||||
// DecodeCookie : Encoding & Decoding of the cookie value
|
||||
func DecodeCookie(cookieValue string) (uint, error) {
|
||||
value := make(map[string]string)
|
||||
err := cookieHandler.Decode(CookieName, cookie_value, &value)
|
||||
err := cookieHandler.Decode(CookieName, cookieValue, &value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
time_int, _ := strconv.ParseInt(value["t"], 10, 0)
|
||||
if timeHelper.IsExpired(time.Unix(time_int, 0)) {
|
||||
timeInt, _ := strconv.ParseInt(value["t"], 10, 0)
|
||||
if timeHelper.IsExpired(time.Unix(timeInt, 0)) {
|
||||
return 0, errors.New("Cookie is expired")
|
||||
}
|
||||
ret, err := strconv.ParseUint(value["u"], 10, 0)
|
||||
return uint(ret), err
|
||||
}
|
||||
|
||||
func EncodeCookie(user_id uint) (string, error) {
|
||||
// EncodeCookie : Encoding of the cookie value
|
||||
func EncodeCookie(userID uint) (string, error) {
|
||||
validUntil := timeHelper.FewDaysLater(7) // 1 week
|
||||
value := map[string]string{
|
||||
"u": strconv.FormatUint(uint64(user_id), 10),
|
||||
"u": strconv.FormatUint(uint64(userID), 10),
|
||||
"t": strconv.FormatInt(validUntil.Unix(), 10),
|
||||
}
|
||||
return cookieHandler.Encode(CookieName, value)
|
||||
}
|
||||
|
||||
// ClearCookie : Erase cookie session
|
||||
func ClearCookie(w http.ResponseWriter) (int, error) {
|
||||
cookie := &http.Cookie{
|
||||
Name: CookieName,
|
||||
|
@ -133,21 +138,20 @@ func CurrentUser(r *http.Request) (model.User, error) {
|
|||
}
|
||||
encoded = cookie.Value
|
||||
}
|
||||
user_id, err := DecodeCookie(encoded)
|
||||
userID, err := DecodeCookie(encoded)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
userFromContext := getUserFromContext(r)
|
||||
|
||||
if userFromContext.ID > 0 && user_id == userFromContext.ID {
|
||||
if userFromContext.ID > 0 && userID == userFromContext.ID {
|
||||
user = userFromContext
|
||||
} else {
|
||||
if db.ORM.Preload("Notifications").Where("user_id = ?", user_id).First(&user).RecordNotFound() { // We only load unread notifications
|
||||
if db.ORM.Preload("Notifications").Where("user_id = ?", userID).First(&user).RecordNotFound() { // We only load unread notifications
|
||||
return user, errors.New("User not found")
|
||||
} else {
|
||||
setUserToContext(r, user)
|
||||
}
|
||||
setUserToContext(r, user)
|
||||
}
|
||||
|
||||
if user.IsBanned() {
|
||||
|
|
|
@ -7,11 +7,12 @@ import (
|
|||
msg "github.com/NyaaPantsu/nyaa/util/messages"
|
||||
)
|
||||
|
||||
const EMAIL_REGEX = `(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`
|
||||
const USERNAME_REGEX = `(\W)`
|
||||
const emailRegex = `(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`
|
||||
const usernameRegex = `(\W)`
|
||||
|
||||
// EmailValidation : Check if an email is valid
|
||||
func EmailValidation(email string, mes *msg.Messages) bool {
|
||||
exp, errorRegex := regexp.Compile(EMAIL_REGEX)
|
||||
exp, errorRegex := regexp.Compile(emailRegex)
|
||||
if regexpCompiled := log.CheckError(errorRegex); regexpCompiled {
|
||||
if exp.MatchString(email) {
|
||||
return true
|
||||
|
@ -21,8 +22,9 @@ func EmailValidation(email string, mes *msg.Messages) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// ValidateUsername : Check if a username is valid
|
||||
func ValidateUsername(username string, mes *msg.Messages) bool {
|
||||
exp, errorRegex := regexp.Compile(USERNAME_REGEX)
|
||||
exp, errorRegex := regexp.Compile(usernameRegex)
|
||||
if regexpCompiled := log.CheckError(errorRegex); regexpCompiled {
|
||||
if exp.MatchString(username) {
|
||||
mes.AddError("username", "Username contains illegal characters")
|
||||
|
@ -34,15 +36,7 @@ func ValidateUsername(username string, mes *msg.Messages) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func NewErrors() map[string][]string {
|
||||
err := make(map[string][]string)
|
||||
return err
|
||||
}
|
||||
func NewInfos() map[string][]string {
|
||||
infos := make(map[string][]string)
|
||||
return infos
|
||||
}
|
||||
|
||||
// IsAgreed : Check if terms and conditions are valid
|
||||
func IsAgreed(termsAndConditions string) bool { // TODO: Inline function
|
||||
return termsAndConditions == "1"
|
||||
}
|
||||
|
@ -65,13 +59,13 @@ type LoginForm struct {
|
|||
|
||||
// UserForm is used when updating a user.
|
||||
type UserForm struct {
|
||||
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
|
||||
Email string `form:"email"`
|
||||
Language string `form:"language" default:"en-us"`
|
||||
CurrentPassword string `form:"current_password" len_min:"6" len_max:"72" omit:"true"`
|
||||
Password string `form:"password" len_min:"6" len_max:"72" equalInput:"Confirm_Password"`
|
||||
Confirm_Password string `form:"password_confirmation" omit:"true"`
|
||||
Status int `form:"status" default:"0"`
|
||||
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
|
||||
Email string `form:"email"`
|
||||
Language string `form:"language" default:"en-us"`
|
||||
CurrentPassword string `form:"current_password" len_min:"6" len_max:"72" omit:"true"`
|
||||
Password string `form:"password" len_min:"6" len_max:"72" equalInput:"ConfirmPassword"`
|
||||
ConfirmPassword string `form:"password_confirmation" omit:"true"`
|
||||
Status int `form:"status" default:"0"`
|
||||
}
|
||||
|
||||
// UserSettingsForm is used when updating a user.
|
||||
|
|
|
@ -23,11 +23,13 @@ func CurrentUserIdentical(user *model.User, userID uint) bool {
|
|||
return user.ID == userID
|
||||
}
|
||||
|
||||
// NeedsCaptcha : Check if a user needs captcha
|
||||
func NeedsCaptcha(user *model.User) bool {
|
||||
// Trusted members & Moderators don't
|
||||
return !(user.IsTrusted() || user.IsModerator())
|
||||
}
|
||||
|
||||
// GetRole : Get the status/role of a user
|
||||
func GetRole(user *model.User) string {
|
||||
switch user.Status {
|
||||
case model.UserStatusBanned:
|
||||
|
@ -42,6 +44,7 @@ func GetRole(user *model.User) string {
|
|||
return "Member"
|
||||
}
|
||||
|
||||
// IsFollower : Check if a user is following another
|
||||
func IsFollower(user *model.User, currentUser *model.User) bool {
|
||||
var likingUserCount int
|
||||
db.ORM.Model(&model.UserFollows{}).Where("user_id = ? and following = ?", user.ID, currentUser.ID).Count(&likingUserCount)
|
||||
|
|
|
@ -17,12 +17,15 @@ import (
|
|||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// NewCurrentUserRetriever create CurrentUserRetriever Struct for languages
|
||||
func NewCurrentUserRetriever() *CurrentUserRetriever {
|
||||
return &CurrentUserRetriever{}
|
||||
}
|
||||
|
||||
// CurrentUserRetriever struct for languages
|
||||
type CurrentUserRetriever struct{}
|
||||
|
||||
// RetrieveCurrentUser retrieve current user for languages
|
||||
func (*CurrentUserRetriever) RetrieveCurrentUser(r *http.Request) (model.User, error) {
|
||||
user, _, err := RetrieveCurrentUser(r)
|
||||
return user, err
|
||||
|
@ -51,6 +54,7 @@ func SuggestUsername(username string) string {
|
|||
return usernameCandidate
|
||||
}
|
||||
|
||||
// CheckEmail : check if email is in database
|
||||
func CheckEmail(email string) bool {
|
||||
if len(email) == 0 {
|
||||
return false
|
||||
|
@ -84,8 +88,8 @@ func CreateUserFromForm(registrationForm formStruct.RegistrationForm) (model.Use
|
|||
user.Settings.ToDefault()
|
||||
user.SaveSettings()
|
||||
// currently unused but needs to be set:
|
||||
user.ApiToken, _ = crypto.GenerateRandomToken32()
|
||||
user.ApiTokenExpiry = time.Unix(0, 0)
|
||||
user.APIToken, _ = crypto.GenerateRandomToken32()
|
||||
user.APITokenExpiry = time.Unix(0, 0)
|
||||
|
||||
if db.ORM.Create(&user).Error != nil {
|
||||
return user, errors.New("user not created")
|
||||
|
@ -232,7 +236,7 @@ func DeleteUser(w http.ResponseWriter, currentUser *model.User, id string) (int,
|
|||
return http.StatusNotFound, errors.New("user not found")
|
||||
}
|
||||
if user.ID == 0 {
|
||||
return http.StatusInternalServerError, errors.New("You can't delete that!")
|
||||
return http.StatusInternalServerError, errors.New("You can't delete that")
|
||||
}
|
||||
if db.ORM.Delete(&user).Error != nil {
|
||||
return http.StatusInternalServerError, errors.New("user not deleted")
|
||||
|
@ -282,6 +286,7 @@ func RetrieveUserByUsername(username string) (*model.PublicUser, string, int, er
|
|||
return &model.PublicUser{User: &user}, username, http.StatusOK, nil
|
||||
}
|
||||
|
||||
// RetrieveOldUploadsByUsername retrieves olduploads by username
|
||||
func RetrieveOldUploadsByUsername(username string) ([]uint, error) {
|
||||
var ret []uint
|
||||
var tmp []*model.UserUploadsOld
|
||||
|
@ -290,7 +295,7 @@ func RetrieveOldUploadsByUsername(username string) ([]uint, error) {
|
|||
return ret, err
|
||||
}
|
||||
for _, tmp2 := range tmp {
|
||||
ret = append(ret, tmp2.TorrentId)
|
||||
ret = append(ret, tmp2.TorrentID)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
@ -318,12 +323,15 @@ func RetrieveUsersForAdmin(limit int, offset int) ([]model.User, int) {
|
|||
return users, nbUsers
|
||||
}
|
||||
|
||||
// GetLiked : Gets who is following the user
|
||||
func GetLiked(user *model.User) *model.User {
|
||||
var liked []model.User
|
||||
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.Liked = liked
|
||||
return user
|
||||
}
|
||||
|
||||
// GetLikings : Gets who is followed by the user
|
||||
func GetLikings(user *model.User) *model.User {
|
||||
var 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)
|
||||
|
@ -341,6 +349,7 @@ func CreateUserAuthentication(w http.ResponseWriter, r *http.Request) (int, erro
|
|||
return status, err
|
||||
}
|
||||
|
||||
// SetFollow : Makes a user follow another
|
||||
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}
|
||||
|
@ -348,20 +357,10 @@ func SetFollow(user *model.User, follower *model.User) {
|
|||
}
|
||||
}
|
||||
|
||||
// RemoveFollow : Remove a user following another
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteComment(id string) (int, error) {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -18,15 +18,15 @@ import (
|
|||
|
||||
var verificationHandler = securecookie.New(config.EmailTokenHashKey, nil)
|
||||
|
||||
// SendEmailVerfication sends an email verification token via email.
|
||||
// SendEmailVerification sends an email verification token via email.
|
||||
func SendEmailVerification(to string, token string) error {
|
||||
T, err := languages.GetDefaultTfunc()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content := T("link") + " : https://" + config.WebAddress + "/verify/email/" + token
|
||||
content_html := T("verify_email_content") + "<br/>" + "<a href=\"https://" + config.WebAddress + "/verify/email/" + token + "\" target=\"_blank\">" + config.WebAddress + "/verify/email/" + token + "</a>"
|
||||
return email.SendEmailFromAdmin(to, T("verify_email_title"), content, content_html)
|
||||
contentHTML := T("verify_email_content") + "<br/>" + "<a href=\"https://" + config.WebAddress + "/verify/email/" + token + "\" target=\"_blank\">" + config.WebAddress + "/verify/email/" + token + "</a>"
|
||||
return email.SendEmailFromAdmin(to, T("verify_email_title"), content, contentHTML)
|
||||
}
|
||||
|
||||
// SendVerificationToUser sends an email verification token to user.
|
||||
|
@ -54,15 +54,15 @@ func EmailVerification(token string, w http.ResponseWriter) (int, error) {
|
|||
err := verificationHandler.Decode("", token, &value)
|
||||
if err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
return http.StatusForbidden, errors.New("Token is not valid.")
|
||||
return http.StatusForbidden, errors.New("Token is not valid")
|
||||
}
|
||||
time_int, _ := strconv.ParseInt(value["t"], 10, 0)
|
||||
if timeHelper.IsExpired(time.Unix(time_int, 0)) {
|
||||
return http.StatusForbidden, errors.New("Token has expired.")
|
||||
timeInt, _ := strconv.ParseInt(value["t"], 10, 0)
|
||||
if timeHelper.IsExpired(time.Unix(timeInt, 0)) {
|
||||
return http.StatusForbidden, errors.New("Token has expired")
|
||||
}
|
||||
var user model.User
|
||||
if db.ORM.Where("user_id = ?", value["u"]).First(&user).RecordNotFound() {
|
||||
return http.StatusNotFound, errors.New("User is not found.")
|
||||
return http.StatusNotFound, errors.New("User is not found")
|
||||
}
|
||||
user.Email = value["e"]
|
||||
return UpdateUserCore(&user)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<div class="user-edit">
|
||||
<form role="form" method="POST">
|
||||
<label class="input-label">{{call $.T "api_token" }}:</label>
|
||||
<p style="font-family: monospace;">{{.ApiToken}}</p>
|
||||
<p style="font-family: monospace;">{{.APIToken}}</p>
|
||||
<button class="btn btn-default" href="{{ genRoute "user_profile_apireset" "id" (print .ID) "username" .Username }}" value="Reset Api key"></button>
|
||||
<label class="input-label">{{ call $.T "email_address" }}:</label> <br>
|
||||
<input class="form-input" type="text" name="email" id="email" value="{{.Email}}"> <br>
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
var categories map[string]string
|
||||
|
||||
// GetCategories : function to get all categories depending on the actual website from config/categories.go
|
||||
func GetCategories() map[string]string {
|
||||
if categories != nil {
|
||||
return categories
|
||||
|
@ -20,11 +21,13 @@ func GetCategories() map[string]string {
|
|||
return categories
|
||||
}
|
||||
|
||||
// CategoryExists : Check if a category exist in config
|
||||
func CategoryExists(category string) bool {
|
||||
_, exists := GetCategories()[category]
|
||||
return exists
|
||||
}
|
||||
|
||||
// GetCategoriesSelect : Format categories in map ordered alphabetically
|
||||
func GetCategoriesSelect(keepParent bool) map[string]string {
|
||||
categories := GetCategories()
|
||||
catSelect := make(map[string]string, len(categories))
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// GenerateMD5Hash : Generate a md5 hash from a string
|
||||
func GenerateMD5Hash(str string) (string, error) {
|
||||
str = strings.ToLower(strings.TrimSpace(str))
|
||||
hash := md5.New()
|
||||
|
@ -17,14 +18,17 @@ func GenerateMD5Hash(str string) (string, error) {
|
|||
return fmt.Sprintf("%x", hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// GenerateRandomToken16 : Generates a random token 16bits long
|
||||
func GenerateRandomToken16() (string, error) {
|
||||
return GenerateRandomToken(16)
|
||||
}
|
||||
|
||||
// GenerateRandomToken32 : Generates a random token 32bits long
|
||||
func GenerateRandomToken32() (string, error) {
|
||||
return GenerateRandomToken(32)
|
||||
}
|
||||
|
||||
// GenerateRandomToken : Generates a random token int n long
|
||||
func GenerateRandomToken(n int) (string, error) {
|
||||
token := make([]byte, n)
|
||||
_, err := rand.Read(token)
|
||||
|
|
|
@ -8,17 +8,20 @@ import (
|
|||
gomail "gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
type EmailError error
|
||||
// Error type
|
||||
type Error error
|
||||
|
||||
var (
|
||||
mailer = InitGomail()
|
||||
)
|
||||
|
||||
// InitGomail : init the gomail dialer
|
||||
func InitGomail() *gomail.Dialer {
|
||||
newMailer := gomail.NewDialer(config.EmailHost, config.EmailPort, config.EmailUsername, config.EmailPassword)
|
||||
return newMailer
|
||||
}
|
||||
|
||||
// SendEmailFromAdmin : send an email from system with email address in config/email.go
|
||||
func SendEmailFromAdmin(to string, subject string, body string, bodyHTML string) error {
|
||||
msg := gomail.NewMessage()
|
||||
msg.SetHeader("From", config.EmailFrom)
|
||||
|
@ -39,6 +42,7 @@ func SendEmailFromAdmin(to string, subject string, body string, bodyHTML string)
|
|||
return nil
|
||||
}
|
||||
|
||||
// SendTestEmail : function to send a test email to email address in config/email.go
|
||||
func SendTestEmail() error {
|
||||
msg := gomail.NewMessage()
|
||||
msg.SetHeader("From", config.EmailFrom)
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
package filelist
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/NyaaPantsu/nyaa/model"
|
||||
"github.com/bradfitz/slice"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FileListFolder struct
|
||||
type FileListFolder struct {
|
||||
Folders []*FileListFolder
|
||||
Files []model.File
|
||||
FolderName string
|
||||
}
|
||||
|
||||
// FileListToFolder convert a filelist to filelistfolder
|
||||
func FileListToFolder(fileList []model.File, folderName string) (out *FileListFolder) {
|
||||
out = &FileListFolder{
|
||||
Folders: make([]*FileListFolder, 0),
|
||||
|
@ -52,6 +55,7 @@ func FileListToFolder(fileList []model.File, folderName string) (out *FileListFo
|
|||
return
|
||||
}
|
||||
|
||||
// TotalSize : gives the total size of filelistfolder
|
||||
func (f *FileListFolder) TotalSize() (out int64) {
|
||||
out = 0
|
||||
for _, folder := range f.Folders {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// FormatFilesize : format file size
|
||||
func FormatFilesize(bytes int64) string {
|
||||
var unit string
|
||||
var value float64
|
||||
|
|
|
@ -14,19 +14,20 @@ import (
|
|||
"github.com/nicksnyder/go-i18n/i18n/language"
|
||||
)
|
||||
|
||||
// this interface is required to prevent a cyclic import between the languages and userService package.
|
||||
// UserRetriever : this interface is required to prevent a cyclic import between the languages and userService package.
|
||||
type UserRetriever interface {
|
||||
RetrieveCurrentUser(r *http.Request) (model.User, error)
|
||||
}
|
||||
|
||||
// TemplateTfunc : T func used in template
|
||||
type TemplateTfunc func(string, ...interface{}) template.HTML
|
||||
|
||||
var (
|
||||
defaultLanguage string = config.DefaultI18nConfig.DefaultLanguage
|
||||
userRetriever UserRetriever = nil
|
||||
defaultLanguage = config.DefaultI18nConfig.DefaultLanguage
|
||||
userRetriever UserRetriever
|
||||
)
|
||||
|
||||
// Initialize the languages translation
|
||||
// InitI18n : Initialize the languages translation
|
||||
func InitI18n(conf config.I18nConfig, retriever UserRetriever) error {
|
||||
defaultLanguage = conf.DefaultLanguage
|
||||
userRetriever = retriever
|
||||
|
@ -52,11 +53,12 @@ func InitI18n(conf config.I18nConfig, retriever UserRetriever) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultLanguage : returns the default language from config
|
||||
func GetDefaultLanguage() string {
|
||||
return defaultLanguage
|
||||
}
|
||||
|
||||
// When go-i18n finds a language with >0 translations, it uses it as the Tfunc
|
||||
// TfuncAndLanguageWithFallback : When go-i18n finds a language with >0 translations, it uses it as the Tfunc
|
||||
// However, if said language has a missing translation, it won't fallback to the "main" language
|
||||
func TfuncAndLanguageWithFallback(language string, languages ...string) (i18n.TranslateFunc, *language.Language, error) {
|
||||
fallbackLanguage := GetDefaultLanguage()
|
||||
|
@ -81,6 +83,7 @@ func TfuncAndLanguageWithFallback(language string, languages ...string) (i18n.Tr
|
|||
return translateFunction, tLang, err1
|
||||
}
|
||||
|
||||
// GetAvailableLanguages : Get languages available on the website
|
||||
func GetAvailableLanguages() (languages map[string]string) {
|
||||
languages = make(map[string]string)
|
||||
var T i18n.TranslateFunc
|
||||
|
@ -97,10 +100,12 @@ func GetAvailableLanguages() (languages map[string]string) {
|
|||
return
|
||||
}
|
||||
|
||||
// GetDefaultTfunc : Gets T func from default language
|
||||
func GetDefaultTfunc() (i18n.TranslateFunc, error) {
|
||||
return i18n.Tfunc(defaultLanguage)
|
||||
}
|
||||
|
||||
// GetTfuncAndLanguageFromRequest : Gets the T func and chosen language from the request
|
||||
func GetTfuncAndLanguageFromRequest(r *http.Request) (T i18n.TranslateFunc, Tlang *language.Language) {
|
||||
userLanguage := ""
|
||||
user, _ := getCurrentUser(r)
|
||||
|
@ -120,6 +125,7 @@ func GetTfuncAndLanguageFromRequest(r *http.Request) (T i18n.TranslateFunc, Tlan
|
|||
return
|
||||
}
|
||||
|
||||
// GetTfuncFromRequest : Gets the T func from the request
|
||||
func GetTfuncFromRequest(r *http.Request) TemplateTfunc {
|
||||
T, _ := GetTfuncAndLanguageFromRequest(r)
|
||||
return func(id string, args ...interface{}) template.HTML {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
func TestInitI18n(t *testing.T) {
|
||||
conf := config.DefaultI18nConfig
|
||||
conf.TranslationsDirectory = path.Join("..", "..", conf.TranslationsDirectory)
|
||||
var retriever UserRetriever = nil // not required during initialization
|
||||
var retriever UserRetriever // not required during initialization
|
||||
|
||||
err := InitI18n(conf, retriever)
|
||||
if err != nil {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
lumberjack "gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
// LumberJackLogger : Initialize logger
|
||||
func LumberJackLogger(filePath string, maxSize int, maxBackups int, maxAge int) *lumberjack.Logger {
|
||||
return &lumberjack.Logger{
|
||||
Filename: filePath,
|
||||
|
@ -18,18 +19,21 @@ func LumberJackLogger(filePath string, maxSize int, maxBackups int, maxAge int)
|
|||
}
|
||||
}
|
||||
|
||||
// InitLogToStdoutDebug : set logrus to debug param
|
||||
func InitLogToStdoutDebug() {
|
||||
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
||||
logrus.SetOutput(os.Stdout)
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
// InitLogToStdout : set logrus to stdout
|
||||
func InitLogToStdout() {
|
||||
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||
logrus.SetOutput(os.Stdout)
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
}
|
||||
|
||||
// InitLogToFile : set logrus to output in file
|
||||
func InitLogToFile() {
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||
|
||||
|
@ -112,7 +116,7 @@ func Panicf(msg string, args ...interface{}) {
|
|||
logrus.Panicf(msg, args...)
|
||||
}
|
||||
|
||||
// log response body data for debugging
|
||||
// DebugResponse : log response body data for debugging
|
||||
func DebugResponse(response *http.Response) string {
|
||||
bodyBuffer := make([]byte, 5000)
|
||||
var str string
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// convert a binary infohash to a magnet uri given a display name and tracker urls
|
||||
// InfoHashToMagnet : convert a binary infohash to a magnet uri given a display name and tracker urls
|
||||
func InfoHashToMagnet(ih string, name string, trackers ...string) (str string) {
|
||||
str = fmt.Sprintf("magnet:?xt=urn:btih:%s", ih)
|
||||
if len(name) > 0 {
|
||||
|
|
|
@ -34,6 +34,7 @@ func init() {
|
|||
|
||||
var HtmlMdRenderer md.Renderer
|
||||
|
||||
// MarkdownToHTML : convert markdown to html
|
||||
// TODO: restrict certain types of markdown
|
||||
func MarkdownToHTML(markdown string) template.HTML {
|
||||
if len(markdown) >= 3 && markdown[:3] == ">" {
|
||||
|
@ -45,8 +46,8 @@ func MarkdownToHTML(markdown string) template.HTML {
|
|||
return template.HTML(html)
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanitize a message passed as a string according to a setted model or allowing a set of html tags and output a string
|
||||
// Sanitize :
|
||||
/* Sanitize a message passed as a string according to a setted model or allowing a set of html tags and output a string
|
||||
*/
|
||||
func Sanitize(msg string, elements ...string) string {
|
||||
msg = repairHTMLTags(msg) // We repair possible broken html tags
|
||||
|
@ -263,8 +264,8 @@ func Sanitize(msg string, elements ...string) string {
|
|||
/*
|
||||
* Should close any opened tags and strip any empty end tags
|
||||
*/
|
||||
func repairHTMLTags(brokenHtml string) string {
|
||||
reader := strings.NewReader(brokenHtml)
|
||||
func repairHTMLTags(brokenHTML string) string {
|
||||
reader := strings.NewReader(brokenHTML)
|
||||
root, err := html.Parse(reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
@ -2,14 +2,17 @@ package Messages
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/NyaaPantsu/nyaa/util/languages"
|
||||
"github.com/gorilla/context"
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// MessagesKey : use for context
|
||||
const MessagesKey = "messages"
|
||||
|
||||
// Messages struct
|
||||
type Messages struct {
|
||||
Errors map[string][]string
|
||||
Infos map[string][]string
|
||||
|
@ -17,6 +20,7 @@ type Messages struct {
|
|||
T i18n.TranslateFunc
|
||||
}
|
||||
|
||||
// GetMessages : Initialize or return the messages object from context
|
||||
func GetMessages(r *http.Request) *Messages {
|
||||
if rv := context.Get(r, MessagesKey); rv != nil {
|
||||
mes := rv.(*Messages)
|
||||
|
@ -24,13 +28,13 @@ func GetMessages(r *http.Request) *Messages {
|
|||
mes.T = T
|
||||
mes.r = r
|
||||
return mes
|
||||
} else {
|
||||
context.Set(r, MessagesKey, &Messages{})
|
||||
T, _ := languages.GetTfuncAndLanguageFromRequest(r)
|
||||
return &Messages{make(map[string][]string), make(map[string][]string), r, T}
|
||||
}
|
||||
context.Set(r, MessagesKey, &Messages{})
|
||||
T, _ := languages.GetTfuncAndLanguageFromRequest(r)
|
||||
return &Messages{make(map[string][]string), make(map[string][]string), r, T}
|
||||
}
|
||||
|
||||
// AddError : Add an error in category name with message msg
|
||||
func (mes *Messages) AddError(name string, msg string) {
|
||||
if mes.Errors == nil {
|
||||
mes.Errors = make(map[string][]string)
|
||||
|
@ -38,19 +42,28 @@ func (mes *Messages) AddError(name string, msg string) {
|
|||
mes.Errors[name] = append(mes.Errors[name], msg)
|
||||
mes.setMessagesInContext()
|
||||
}
|
||||
|
||||
// AddErrorf : Add an error in category name with message msg formatted with args
|
||||
func (mes *Messages) AddErrorf(name string, msg string, args ...interface{}) {
|
||||
mes.AddError(name, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
// AddErrorTf : Add an error in category name with translation string id formatted with args
|
||||
func (mes *Messages) AddErrorTf(name string, id string, args ...interface{}) {
|
||||
mes.AddErrorf(name, mes.T(id), args...)
|
||||
}
|
||||
|
||||
// AddErrorT : Add an error in category name with translation string id
|
||||
func (mes *Messages) AddErrorT(name string, id string) {
|
||||
mes.AddError(name, mes.T(id))
|
||||
}
|
||||
|
||||
// ImportFromError : Add an error in category name with message msg imported from type error
|
||||
func (mes *Messages) ImportFromError(name string, err error) {
|
||||
mes.AddError(name, err.Error())
|
||||
}
|
||||
|
||||
// AddInfo : Add an info in category name with message msg
|
||||
func (mes *Messages) AddInfo(name string, msg string) {
|
||||
if mes.Infos == nil {
|
||||
mes.Infos = make(map[string][]string)
|
||||
|
@ -58,47 +71,65 @@ func (mes *Messages) AddInfo(name string, msg string) {
|
|||
mes.Infos[name] = append(mes.Infos[name], msg)
|
||||
mes.setMessagesInContext()
|
||||
}
|
||||
|
||||
// AddInfof : Add an info in category name with message msg formatted with args
|
||||
func (mes *Messages) AddInfof(name string, msg string, args ...interface{}) {
|
||||
mes.AddInfo(name, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
// AddInfoTf : Add an info in category name with translation string id formatted with args
|
||||
func (mes *Messages) AddInfoTf(name string, id string, args ...interface{}) {
|
||||
mes.AddInfof(name, mes.T(id), args...)
|
||||
}
|
||||
|
||||
// AddInfoT : Add an info in category name with translation string id
|
||||
func (mes *Messages) AddInfoT(name string, id string) {
|
||||
mes.AddInfo(name, mes.T(id))
|
||||
}
|
||||
|
||||
// ClearErrors : Erase all errors in messages
|
||||
func (mes *Messages) ClearErrors() {
|
||||
mes.Infos = nil
|
||||
mes.setMessagesInContext()
|
||||
}
|
||||
|
||||
// ClearInfos : Erase all infos in messages
|
||||
func (mes *Messages) ClearInfos() {
|
||||
mes.Errors = nil
|
||||
mes.setMessagesInContext()
|
||||
}
|
||||
|
||||
// GetAllErrors : Get all errors
|
||||
func (mes *Messages) GetAllErrors() map[string][]string {
|
||||
mes = GetMessages(mes.r) // We need to look if any new errors from other functions has updated context
|
||||
return mes.Errors
|
||||
}
|
||||
|
||||
// GetErrors : Get all errors in category name
|
||||
func (mes *Messages) GetErrors(name string) []string {
|
||||
mes = GetMessages(mes.r) // We need to look if any new errors from other functions has updated context
|
||||
return mes.Errors[name]
|
||||
}
|
||||
|
||||
// GetAllInfos : Get all infos
|
||||
func (mes *Messages) GetAllInfos() map[string][]string {
|
||||
mes = GetMessages(mes.r) // We need to look if any new errors from other functions has updated context
|
||||
return mes.Infos
|
||||
}
|
||||
|
||||
// GetInfos : Get all infos in category name
|
||||
func (mes *Messages) GetInfos(name string) []string {
|
||||
mes = GetMessages(mes.r) // We need to look if any new errors from other functions has updated context
|
||||
return mes.Infos[name]
|
||||
}
|
||||
|
||||
// HasErrors : Check if there are errors
|
||||
func (mes *Messages) HasErrors() bool {
|
||||
mes = GetMessages(mes.r) // We need to look if any new errors from other functions has updated context
|
||||
return len(mes.Errors) > 0
|
||||
}
|
||||
|
||||
// HasInfos : Check if there are infos
|
||||
func (mes *Messages) HasInfos() bool {
|
||||
mes = GetMessages(mes.r) // We need to look if any new errors from other functions has updated context
|
||||
return len(mes.Infos) > 0
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package metainfo
|
||||
|
||||
/**
|
||||
package for parsing bitorrent meta info objects
|
||||
originally from XD i2p bittorrent client: https://github.com/majestrate/XD
|
||||
*/
|
||||
package metainfo
|
||||
|
|
|
@ -4,5 +4,5 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
// error for indicating we have an invalid torrent file
|
||||
// ErrInvalidTorrentFile : error for indicating we have an invalid torrent file
|
||||
var ErrInvalidTorrentFile = errors.New("invalid bittorrent file")
|
||||
|
|
|
@ -11,18 +11,20 @@ import (
|
|||
"github.com/zeebo/bencode"
|
||||
)
|
||||
|
||||
// FilePath type
|
||||
type FilePath []string
|
||||
|
||||
// get filepath
|
||||
// FilePath : get filepath
|
||||
func (f FilePath) FilePath() string {
|
||||
return filepath.Join(f...)
|
||||
}
|
||||
|
||||
/** open file using base path */
|
||||
// Open : open file using base path
|
||||
func (f FilePath) Open(base string) (*os.File, error) {
|
||||
return os.OpenFile(filepath.Join(base, f.FilePath()), os.O_RDWR|os.O_CREATE, 0600)
|
||||
}
|
||||
|
||||
// FileInfo struct
|
||||
type FileInfo struct {
|
||||
// length of file
|
||||
Length uint64 `bencode:"length"`
|
||||
|
@ -32,7 +34,7 @@ type FileInfo struct {
|
|||
Sum []byte `bencode:"md5sum,omitempty"`
|
||||
}
|
||||
|
||||
// info section of torrent file
|
||||
// Info : info section of torrent file
|
||||
type Info struct {
|
||||
// length of pices in bytes
|
||||
PieceLength uint32 `bencode:"piece length"`
|
||||
|
@ -50,7 +52,7 @@ type Info struct {
|
|||
Sum []byte `bencode:"md5sum,omitempty"`
|
||||
}
|
||||
|
||||
// get fileinfos from this info section
|
||||
// GetFiles : get fileinfos from this info section
|
||||
func (i Info) GetFiles() (infos []FileInfo) {
|
||||
if i.Length > 0 {
|
||||
infos = append(infos, FileInfo{
|
||||
|
@ -64,11 +66,12 @@ func (i Info) GetFiles() (infos []FileInfo) {
|
|||
return
|
||||
}
|
||||
|
||||
// NumPieces : length of Info
|
||||
func (i Info) NumPieces() uint32 {
|
||||
return uint32(len(i.Pieces) / 20)
|
||||
}
|
||||
|
||||
// a torrent file
|
||||
// TorrentFile : a torrent file
|
||||
type TorrentFile struct {
|
||||
Info Info `bencode:"info"`
|
||||
Announce string `bencode:"announce"`
|
||||
|
@ -79,7 +82,7 @@ type TorrentFile struct {
|
|||
Encoding []byte `bencode:"encoding"`
|
||||
}
|
||||
|
||||
// get total size of files from torrent info section
|
||||
// TotalSize : get total size of files from torrent info section
|
||||
func (tf *TorrentFile) TotalSize() uint64 {
|
||||
if tf.IsSingleFile() {
|
||||
return tf.Info.Length
|
||||
|
@ -91,6 +94,7 @@ func (tf *TorrentFile) TotalSize() uint64 {
|
|||
return total
|
||||
}
|
||||
|
||||
// GetAllAnnounceURLS : get all trackers url
|
||||
func (tf *TorrentFile) GetAllAnnounceURLS() (l []string) {
|
||||
l = make([]string, 0, 64)
|
||||
if len(tf.Announce) > 0 {
|
||||
|
@ -106,16 +110,17 @@ func (tf *TorrentFile) GetAllAnnounceURLS() (l []string) {
|
|||
return
|
||||
}
|
||||
|
||||
// TorrentName : return torrent name
|
||||
func (tf *TorrentFile) TorrentName() string {
|
||||
return tf.Info.Path
|
||||
}
|
||||
|
||||
// return true if this torrent is private otherwise return false
|
||||
// IsPrivate : return true if this torrent is private otherwise return false
|
||||
func (tf *TorrentFile) IsPrivate() bool {
|
||||
return tf.Info.Private != nil && *tf.Info.Private == 1
|
||||
}
|
||||
|
||||
// calculate infohash
|
||||
// Infohash : calculate infohash
|
||||
func (tf *TorrentFile) Infohash() (ih [20]byte, err error) {
|
||||
s := sha1.New()
|
||||
enc := bencode.NewEncoder(s)
|
||||
|
@ -128,19 +133,19 @@ func (tf *TorrentFile) Infohash() (ih [20]byte, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// return true if this torrent is for a single file
|
||||
// IsSingleFile : return true if this torrent is for a single file
|
||||
func (tf *TorrentFile) IsSingleFile() bool {
|
||||
return tf.Info.Length > 0
|
||||
}
|
||||
|
||||
// bencode this file via an io.Writer
|
||||
// Encode : bencode this file via an io.Writer
|
||||
func (tf *TorrentFile) Encode(w io.Writer) (err error) {
|
||||
enc := bencode.NewEncoder(w)
|
||||
err = enc.Encode(tf)
|
||||
return
|
||||
}
|
||||
|
||||
// load from an io.Reader
|
||||
// Decode : load from an io.Reader
|
||||
func (tf *TorrentFile) Decode(r io.Reader) (err error) {
|
||||
dec := bencode.NewDecoder(r)
|
||||
err = dec.Decode(tf)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package modelHelper
|
||||
|
||||
import (
|
||||
"github.com/NyaaPantsu/nyaa/util/log"
|
||||
msg "github.com/NyaaPantsu/nyaa/util/messages"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/NyaaPantsu/nyaa/util/log"
|
||||
msg "github.com/NyaaPantsu/nyaa/util/messages"
|
||||
)
|
||||
|
||||
// TODO: Rewrite module
|
||||
|
@ -34,7 +35,7 @@ func AssignValue(model interface{}, form interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
// AssignValue assign form values to model.
|
||||
// BindValueForm assign populate form from a request
|
||||
func BindValueForm(form interface{}, r *http.Request) {
|
||||
r.ParseForm()
|
||||
formElem := reflect.ValueOf(form).Elem()
|
||||
|
@ -57,6 +58,7 @@ func BindValueForm(form interface{}, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// ValidateForm : Check if a form is valid according to its tags
|
||||
func ValidateForm(form interface{}, mes *msg.Messages) {
|
||||
formElem := reflect.ValueOf(form).Elem()
|
||||
for i := 0; i < formElem.NumField(); i++ {
|
||||
|
@ -105,7 +107,7 @@ func ValidateForm(form interface{}, mes *msg.Messages) {
|
|||
if tag.Get("needed") != "" && formElem.Field(i).Int() == 0 {
|
||||
mes.AddErrorf(tag.Get("form"), "Field needed: %s", inputName)
|
||||
}
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" { // FIXME: always false :'(
|
||||
defaultValue, _ := strconv.Atoi(tag.Get("default"))
|
||||
formElem.Field(i).SetInt(int64(defaultValue))
|
||||
}
|
||||
|
@ -119,7 +121,7 @@ func ValidateForm(form interface{}, mes *msg.Messages) {
|
|||
if tag.Get("needed") != "" && formElem.Field(i).Float() == 0 {
|
||||
mes.AddErrorf(tag.Get("form"), "Field needed: %s", inputName)
|
||||
}
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" { // FIXME: always false :'(
|
||||
defaultValue, _ := strconv.Atoi(tag.Get("default"))
|
||||
formElem.Field(i).SetFloat(float64(defaultValue))
|
||||
}
|
||||
|
@ -130,7 +132,7 @@ func ValidateForm(form interface{}, mes *msg.Messages) {
|
|||
mes.AddErrorf(tag.Get("form"), "Wrong value for the input: %s", inputName)
|
||||
}
|
||||
}
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" { // FIXME: always false :'(
|
||||
defaultValue, _ := strconv.ParseBool(tag.Get("default"))
|
||||
formElem.Field(i).SetBool(defaultValue)
|
||||
}
|
||||
|
|
|
@ -5,10 +5,12 @@ import (
|
|||
"html/template"
|
||||
)
|
||||
|
||||
// Safe : make un url safe
|
||||
func Safe(s string) template.URL {
|
||||
return template.URL(html.EscapeString(s))
|
||||
}
|
||||
|
||||
// SafeText : make a string safe
|
||||
func SafeText(s string) template.HTML {
|
||||
return template.HTML(html.EscapeString(s))
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
var searchOperator string
|
||||
var useTSQuery bool
|
||||
|
||||
// Configure : initialize search
|
||||
func Configure(conf *config.SearchConfig) (err error) {
|
||||
useTSQuery = false
|
||||
// Postgres needs ILIKE for case-insensitivity
|
||||
|
@ -35,7 +36,7 @@ func Configure(conf *config.SearchConfig) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func stringIsAscii(input string) bool {
|
||||
func stringIsASCII(input string) bool {
|
||||
for _, char := range input {
|
||||
if char > 127 {
|
||||
return false
|
||||
|
@ -44,21 +45,25 @@ func stringIsAscii(input string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// SearchByQuery : search torrents according to request without user
|
||||
func SearchByQuery(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
|
||||
search, tor, count, err = searchByQuery(r, pagenum, true, false, false)
|
||||
return
|
||||
}
|
||||
|
||||
// SearchByQueryWithUser : search torrents according to request with user
|
||||
func SearchByQueryWithUser(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
|
||||
search, tor, count, err = searchByQuery(r, pagenum, true, true, false)
|
||||
return
|
||||
}
|
||||
|
||||
// SearchByQueryNoCount : search torrents according to request without user and count
|
||||
func SearchByQueryNoCount(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, err error) {
|
||||
search, tor, _, err = searchByQuery(r, pagenum, false, false, false)
|
||||
return
|
||||
}
|
||||
|
||||
// SearchByQueryDeleted : search deleted torrents according to request with user and count
|
||||
func SearchByQueryDeleted(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
|
||||
search, tor, count, err = searchByQuery(r, pagenum, true, true, true)
|
||||
return
|
||||
|
@ -239,7 +244,7 @@ func searchByQueryPostgres(r *http.Request, pagenum int, countAll bool, withUser
|
|||
continue
|
||||
}
|
||||
|
||||
if useTSQuery && stringIsAscii(word) {
|
||||
if useTSQuery && stringIsASCII(word) {
|
||||
conditions = append(conditions, "torrent_name @@ plainto_tsquery(?)")
|
||||
parameters.Params = append(parameters.Params, word)
|
||||
} else {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/util/log"
|
||||
)
|
||||
|
||||
// SendError : Send an error as response
|
||||
func SendError(w http.ResponseWriter, err error, code int) {
|
||||
log.Warnf("%s:\n%s\n", err, debug.Stack())
|
||||
http.Error(w, err.Error(), code)
|
||||
|
|
|
@ -6,28 +6,34 @@ import (
|
|||
"github.com/NyaaPantsu/nyaa/util/log"
|
||||
)
|
||||
|
||||
// FewDaysLater : Give time now + some days
|
||||
func FewDaysLater(day int) time.Time {
|
||||
return FewDurationLater(time.Duration(day) * 24 * time.Hour)
|
||||
}
|
||||
|
||||
// TwentyFourHoursLater : Give time now + 24 hours
|
||||
func TwentyFourHoursLater() time.Time {
|
||||
return FewDurationLater(time.Duration(24) * time.Hour)
|
||||
}
|
||||
|
||||
// SixHoursLater : Give time now + 6 hours
|
||||
func SixHoursLater() time.Time {
|
||||
return FewDurationLater(time.Duration(6) * time.Hour)
|
||||
}
|
||||
|
||||
// InTimeSpan : check if time given is in the given time encapsulation
|
||||
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)
|
||||
}
|
||||
|
||||
// InTimeSpanNow : check if time now is in the given time encapsulation
|
||||
func InTimeSpanNow(start, end time.Time) bool {
|
||||
now := time.Now()
|
||||
return InTimeSpan(start, end, now)
|
||||
}
|
||||
|
||||
// FewDurationLater : Give time now + some time duration
|
||||
func FewDurationLater(duration time.Duration) time.Time {
|
||||
// When Save time should considering UTC
|
||||
fewDurationLater := time.Now().Add(duration)
|
||||
|
@ -35,10 +41,12 @@ func FewDurationLater(duration time.Duration) time.Time {
|
|||
return fewDurationLater
|
||||
}
|
||||
|
||||
// FewDurationLaterMillisecond : Give time now + some millisecond
|
||||
func FewDurationLaterMillisecond(duration time.Duration) int64 {
|
||||
return FewDurationLater(duration).UnixNano() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
// IsExpired : check if time given is expired
|
||||
func IsExpired(expirationTime time.Time) bool {
|
||||
log.Debugf("expirationTime : %s", expirationTime)
|
||||
after := time.Now().After(expirationTime)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"io/ioutil"
|
||||
)
|
||||
|
||||
// UnZlib : Is it deprecated?
|
||||
func UnZlib(description []byte) (string, error) {
|
||||
if len(description) > 0 {
|
||||
b := bytes.NewReader(description)
|
||||
|
|
|
@ -4,12 +4,12 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// return true if r is a whitespace rune
|
||||
// IsWhitespace : return true if r is a whitespace rune
|
||||
func IsWhitespace(r rune) bool {
|
||||
return r == '\n' || r == '\t' || r == '\r' || r == ' '
|
||||
}
|
||||
|
||||
// trim whitespaces from a string
|
||||
// TrimWhitespaces : trim whitespaces from a string
|
||||
func TrimWhitespaces(s string) string {
|
||||
s = strings.TrimLeftFunc(s, IsWhitespace)
|
||||
s = strings.TrimRightFunc(s, IsWhitespace)
|
||||
|
|
Référencer dans un nouveau ticket