Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Merge pull request #1 from ewhal/master

Testing Fork Update
Cette révision appartient à :
Nutjob 2017-05-10 21:12:26 +02:00 révisé par GitHub
révision b067e42b96
57 fichiers modifiés avec 1983 ajouts et 862 suppressions

5
.gitignore externe Fichier normal → Fichier exécutable
Voir le fichier

@ -1,4 +1,5 @@
*.sqlite
*.db-journal
*.db
*.sql
main
@ -14,3 +15,7 @@ templates/*.html.go
*.backup
tags
*.retry
# emacs temp files
*\#*
*~

Voir le fichier

@ -30,7 +30,9 @@ func GormInit(conf *config.Config) (*gorm.DB, error) {
// TODO: Enable Gorm initialization for non-development builds
if config.Environment == "DEVELOPMENT" {
db.LogMode(true)
db.AutoMigrate(&model.Torrent{}, &model.UserFollows{}, &model.User{}, &model.Comment{}, &model.OldComment{}, &model.TorrentReport{})
db.AutoMigrate(&model.User{}, &model.UserFollows{}, &model.UserUploadsOld{})
db.AutoMigrate(&model.Torrent{}, &model.TorrentReport{})
db.AutoMigrate(&model.Comment{}, &model.OldComment{})
}
return db, nil

10
main.go
Voir le fichier

@ -40,9 +40,17 @@ func RunServer(conf *config.Config) {
l, err := network.CreateHTTPListener(conf)
log.CheckError(err)
if err == nil {
// add http server to be closed gracefully
signals.RegisterCloser(&network.GracefulHttpCloser{
Server: srv,
Listener: l,
})
log.Infof("listening on %s", l.Addr())
err := srv.Serve(l)
log.CheckError(err)
if err != nil && err != network.ErrListenerStopped {
log.CheckError(err)
}
}
}

37
model/report.go Fichier normal
Voir le fichier

@ -0,0 +1,37 @@
package model
// TODO Add field to specify kind of reports
// TODO Add CreatedAt field
// INFO User can be null (anonymous reports)
// FIXME can't preload field Torrents for model.TorrentReport
type TorrentReport struct {
ID uint `gorm:"column:torrent_report_id;primary_key"`
Description string `gorm:"column:type"`
TorrentID uint `gorm:"column:torrent_id"`
UserID uint `gorm:"column:user_id"`
Torrent Torrent `gorm:"AssociationForeignKey:TorrentID;ForeignKey:torrent_id"`
User User `gorm:"AssociationForeignKey:UserID;ForeignKey:ID"`
}
type TorrentReportJson struct {
ID uint `json:"id"`
Description string `json:"description"`
Torrent TorrentJSON `json:"torrent"`
User UserJSON `json:"user"`
}
/* Model Conversion to Json */
func (report *TorrentReport) ToJson() TorrentReportJson {
json := TorrentReportJson{report.ID, report.Description, report.Torrent.ToJSON(), report.User.ToJSON()}
return json
}
func TorrentReportsToJSON(reports []TorrentReport) []TorrentReportJson {
json := make([]TorrentReportJson, len(reports))
for i := range reports {
json[i] = reports[i].ToJson()
}
return json
}

Voir le fichier

@ -5,7 +5,6 @@ import (
"github.com/ewhal/nyaa/util"
"fmt"
"html"
"html/template"
"strconv"
"strings"
@ -36,12 +35,14 @@ type Torrent struct {
WebsiteLink string `gorm:"column:website_link"`
DeletedAt *time.Time
Uploader *User `gorm:"ForeignKey:UploaderId"`
Uploader *User `gorm:"ForeignKey:uploader"`
OldUploader string `gorm:"-"` // ???????
OldComments []OldComment `gorm:"ForeignKey:torrent_id"`
Comments []Comment `gorm:"ForeignKey:torrent_id"`
}
// 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
2*3 + // time.Time
@ -66,19 +67,6 @@ func (t Torrent) Size() (s int) {
}
// TODO Add field to specify kind of reports
// TODO Add CreatedAt field
// INFO User can be null (anonymous reports)
// FIXME can't preload field Torrents for model.TorrentReport
type TorrentReport struct {
ID uint `gorm:"column:torrent_report_id;primary_key"`
Description string `gorm:"column:type"`
TorrentID uint
UserID uint
Torrent Torrent `gorm:"AssociationForeignKey:TorrentID;ForeignKey:ID"`
User User `gorm:"AssociationForeignKey:UserID;ForeignKey:ID"`
}
/* We need a JSON object instead of a Gorm structure because magnet URLs are
not in the database and have to be generated dynamically */
@ -108,32 +96,18 @@ type TorrentJSON struct {
Downloads int `json:"downloads"`
UploaderID uint `json:"uploader_id"`
UploaderName template.HTML `json:"uploader_name"`
OldUploader template.HTML `json:"uploader_old"`
WebsiteLink template.URL `json:"website_link"`
Magnet template.URL `json:"magnet"`
TorrentLink template.URL `json:"torrent"`
}
type TorrentReportJson struct {
ID uint `json:"id"`
Description string `json:"description"`
Torrent TorrentJSON `json:"torrent"`
User string
}
/* Model Conversion to Json */
func (report *TorrentReport) ToJson() TorrentReportJson {
json := TorrentReportJson{report.ID, report.Description, report.Torrent.ToJSON(), report.User.Username}
return json
}
// ToJSON converts a model.Torrent to its equivalent JSON structure
func (t *Torrent) ToJSON() TorrentJSON {
magnet := util.InfoHashToMagnet(strings.TrimSpace(t.Hash), t.Name, config.Trackers...)
commentsJSON := make([]CommentJSON, 0, len(t.OldComments)+len(t.Comments))
for _, c := range t.OldComments {
escapedContent := template.HTML(html.EscapeString(c.Content))
commentsJSON = append(commentsJSON, CommentJSON{Username: c.Username, Content: escapedContent, Date: c.Date})
commentsJSON = append(commentsJSON, CommentJSON{Username: c.Username, Content: template.HTML(c.Content), Date: c.Date})
}
for _, c := range t.Comments {
commentsJSON = append(commentsJSON, CommentJSON{Username: c.User.Username, Content: util.MarkdownToHTML(c.Content), Date: c.CreatedAt})
@ -162,6 +136,7 @@ func (t *Torrent) ToJSON() TorrentJSON {
Downloads: t.Downloads,
UploaderID: t.UploaderID,
UploaderName: util.SafeText(uploader),
OldUploader: util.SafeText(t.OldUploader),
WebsiteLink: util.Safe(t.WebsiteLink),
Magnet: util.Safe(magnet),
TorrentLink: util.Safe(torrentlink)}
@ -179,11 +154,3 @@ func TorrentsToJSON(t []Torrent) []TorrentJSON { // TODO: Convert to singular ve
}
return json
}
func TorrentReportsToJSON(reports []TorrentReport) []TorrentReportJson {
json := make([]TorrentReportJson, len(reports))
for i := range reports {
json[i] = reports[i].ToJson()
}
return json
}

Voir le fichier

@ -22,10 +22,19 @@ type User struct {
Likings []User // Don't work `gorm:"foreignkey:user_id;associationforeignkey:follower_id;many2many:user_follows"`
Liked []User // Don't work `gorm:"foreignkey:follower_id;associationforeignkey:user_id;many2many:user_follows"`
MD5 string `json:"md5"` // Hash of email address, used for Gravatar
MD5 string `json:"md5" gorm:"column:md5"` // Hash of email address, used for Gravatar
Torrents []Torrent `gorm:"ForeignKey:UploaderID"`
}
type UserJSON struct {
ID uint `json:"user_id"`
Username string `json:"username"`
Status int `json:"status"`
CreatedAt string `json:"created_at"`
LikingCount int `json:"liking_count"`
LikedCount int `json:"liked_count"`
}
// Returns the total size of memory recursively allocated for this struct
func (u User) Size() (s int) {
s += 4 + // ints
@ -50,3 +59,25 @@ type UserFollows struct {
UserID uint `gorm:"column:user_id"`
FollowerID uint `gorm:"column:following"`
}
type UserUploadsOld struct {
Username string `gorm:"column:username"`
TorrentId uint `gorm:"column:torrent_id"`
}
func (c UserUploadsOld) TableName() string {
// TODO: rename this in db
return "user_uploads_old"
}
func (u *User) ToJSON() UserJSON {
json := UserJSON{
ID: u.ID,
Username: u.Username,
Status: u.Status,
CreatedAt: u.CreatedAt.Format(time.RFC3339),
LikingCount: u.LikingCount,
LikedCount: u.LikedCount,
}
return json
}

17
network/closer.go Fichier normal
Voir le fichier

@ -0,0 +1,17 @@
package network
import (
"net"
"net/http"
)
// implements io.Closer that gracefully closes an http server
type GracefulHttpCloser struct {
Server *http.Server
Listener net.Listener
}
func (c *GracefulHttpCloser) Close() error {
c.Listener.Close()
return c.Server.Shutdown(nil)
}

58
network/graceful.go Fichier normal
Voir le fichier

@ -0,0 +1,58 @@
package network
import (
"errors"
"net"
)
var ErrListenerStopped = errors.New("listener was stopped")
// GracefulListener provides safe and graceful net.Listener wrapper that prevents error on graceful shutdown
type GracefulListener struct {
listener net.Listener
stop chan int
}
func (l *GracefulListener) Accept() (net.Conn, error) {
for {
c, err := l.listener.Accept()
select {
case <-l.stop:
if c != nil {
c.Close()
}
close(l.stop)
l.stop = nil
return nil, ErrListenerStopped
default:
}
if err != nil {
neterr, ok := err.(net.Error)
if ok && neterr.Timeout() && neterr.Temporary() {
continue
}
}
return c, err
}
}
func (l *GracefulListener) Close() (err error) {
l.listener.Close()
if l.stop != nil {
l.stop <- 0
}
return
}
func (l *GracefulListener) Addr() net.Addr {
return l.listener.Addr()
}
// WrapListener wraps a net.Listener such that it can be closed gracefully
func WrapListener(l net.Listener) net.Listener {
return &GracefulListener{
listener: l,
stop: make(chan int),
}
}

Voir le fichier

@ -20,5 +20,8 @@ func CreateHTTPListener(conf *config.Config) (l net.Listener, err error) {
l = s
}
}
if l != nil {
l = WrapListener(l)
}
return
}

Voir le fichier

@ -1,5 +1,3 @@
/* Torrent status colors */
.remake {
background-color: rgb(240, 176, 128);
@ -323,7 +321,8 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
background: #fff;
min-height: 460px;
}
/* Night mode switcher */
/* Night mode switcher */
#mainmenu a.nightswitch {
background: transparent url(/img/sun.png) no-repeat;
background-size: 24px;
@ -331,11 +330,12 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
margin-left: 3px;
width: 24px;
}
footer {
text-align: center;
padding: 2rem;
padding-bottom: 2rem;
font-size: 2rem;
font-family: cursive;
color: #CCC;
text-shadow: 0px -1px #999999;
}
color: #616161;
text-shadow: -1px -1px #999999;
}

Voir le fichier

@ -11,6 +11,7 @@ import (
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service"
"github.com/ewhal/nyaa/service/api"
"github.com/ewhal/nyaa/service/torrent"
"github.com/ewhal/nyaa/util"
@ -21,7 +22,7 @@ import (
func ApiHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
page := vars["page"]
whereParams := torrentService.WhereParams{}
whereParams := serviceBase.WhereParams{}
req := apiService.TorrentsRequest{}
contentType := r.Header.Get("Content-Type")

Voir le fichier

@ -3,30 +3,42 @@
package router
import (
"html"
"html/template"
"net/http"
"path/filepath"
"strconv"
"fmt"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service/comment"
"github.com/ewhal/nyaa/service/report"
"github.com/ewhal/nyaa/service/torrent"
"github.com/ewhal/nyaa/service/torrent/form"
"github.com/ewhal/nyaa/service/user"
form "github.com/ewhal/nyaa/service/user/form"
"github.com/ewhal/nyaa/service/user/permission"
"github.com/ewhal/nyaa/util/languages"
"github.com/ewhal/nyaa/util/log"
"github.com/ewhal/nyaa/util/modelHelper"
"github.com/ewhal/nyaa/util/search"
"github.com/gorilla/mux"
)
var panelIndex, panelTorrentList, panelUserList, panelCommentList, panelTorrentEd *template.Template
var panelIndex, panelTorrentList, panelUserList, panelCommentList, panelTorrentEd, panelTorrentReportList *template.Template
func init() {
panelTorrentList = template.Must(template.New("torrentlist").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/torrentlist.html")))
panelTorrentList = template.Must(panelTorrentList.ParseGlob(filepath.Join("templates", "_*.html")))
panelUserList = template.Must(template.New("userlist").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/userlist.html")))
panelUserList = template.Must(panelUserList.ParseGlob(filepath.Join("templates", "_*.html")))
panelCommentList = template.Must(template.New("commentlist").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/commentlist.html")))
panelCommentList = template.Must(panelCommentList.ParseGlob(filepath.Join("templates", "_*.html")))
panelIndex = template.Must(template.New("indexPanel").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/panelindex.html")))
panelTorrentEd = template.Must(template.New("indexPanel").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/paneltorrentedit.html")))
panelIndex = template.Must(panelIndex.ParseGlob(filepath.Join("templates", "_*.html")))
panelTorrentEd = template.Must(template.New("torrent_ed").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/paneltorrentedit.html")))
panelTorrentEd = template.Must(panelTorrentEd.ParseGlob(filepath.Join("templates", "_*.html")))
panelTorrentReportList = template.Must(template.New("torrent_report").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/torrent_report.html")))
panelTorrentReportList = template.Must(panelTorrentReportList.ParseGlob(filepath.Join("templates", "_*.html")))
}
func IndexModPanel(w http.ResponseWriter, r *http.Request) {
@ -34,58 +46,138 @@ func IndexModPanel(w http.ResponseWriter, r *http.Request) {
if userPermission.HasAdmin(currentUser) {
offset := 10
torrents, _, _ := torrentService.GetAllTorrents(0, offset)
users := userService.RetrieveUsersForAdmin(0, offset)
comments := commentService.GetAllComments(0, offset)
torrents, _, _ := torrentService.GetAllTorrents(offset, 0)
users, _ := userService.RetrieveUsersForAdmin(offset, 0)
comments, _ := commentService.GetAllComments(offset, 0, "", "")
torrentReports, _, _ := reportService.GetAllTorrentReports(offset, 0)
languages.SetTranslationFromRequest(panelIndex, r, "en-us")
htv := PanelIndexVbs{torrents, users, comments}
htv := PanelIndexVbs{torrents, torrentReports, users, comments, NewSearchForm(), currentUser, r.URL}
_ = panelIndex.ExecuteTemplate(w, "admin_index.html", htv)
} else {
http.Error(w, "admins only", http.StatusForbidden)
}
}
func TorrentsListPanel(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if userPermission.HasAdmin(currentUser) {
page, _ := strconv.Atoi(r.URL.Query().Get("p"))
vars := mux.Vars(r)
page := vars["page"]
var err error
pagenum := 1
if page != "" {
pagenum, err = strconv.Atoi(html.EscapeString(page))
if !log.CheckError(err) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
offset := 100
torrents, _, _ := torrentService.GetAllTorrents(offset, page * offset)
searchParam, torrents, _, err := search.SearchByQuery(r, pagenum)
searchForm := SearchForm{
SearchParam: searchParam,
Category: searchParam.Category.String(),
HideAdvancedSearch: false,
}
languages.SetTranslationFromRequest(panelTorrentList, r, "en-us")
htv := PanelTorrentListVbs{torrents}
err := panelTorrentList.ExecuteTemplate(w, "admin_index.html", htv)
fmt.Println(err)
htv := PanelTorrentListVbs{torrents, searchForm, Navigation{int(searchParam.Max), offset, pagenum, "mod_tlist_page"}, currentUser, r.URL}
err = panelTorrentList.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err)
} else {
http.Error(w, "admins only", http.StatusForbidden)
}
}
func TorrentReportListPanel(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if userPermission.HasAdmin(currentUser) {
vars := mux.Vars(r)
page := vars["page"]
var err error
pagenum := 1
if page != "" {
pagenum, err = strconv.Atoi(html.EscapeString(page))
if !log.CheckError(err) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
offset := 100
torrentReports, nbReports, _ := reportService.GetAllTorrentReports(offset, (pagenum-1)*offset)
reportJSON := model.TorrentReportsToJSON(torrentReports)
languages.SetTranslationFromRequest(panelTorrentReportList, r, "en-us")
htv := PanelTorrentReportListVbs{reportJSON, NewSearchForm(), Navigation{nbReports, offset, pagenum, "mod_trlist_page"}, currentUser, r.URL}
err = panelTorrentReportList.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err)
} else {
http.Error(w, "admins only", http.StatusForbidden)
}
}
func UsersListPanel(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if userPermission.HasAdmin(currentUser) {
page, _ := strconv.Atoi(r.URL.Query().Get("p"))
vars := mux.Vars(r)
page := vars["page"]
var err error
pagenum := 1
if page != "" {
pagenum, err = strconv.Atoi(html.EscapeString(page))
if !log.CheckError(err) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
offset := 100
users := userService.RetrieveUsersForAdmin(offset, page*offset)
users, nbUsers := userService.RetrieveUsersForAdmin(offset, (pagenum-1)*offset)
languages.SetTranslationFromRequest(panelUserList, r, "en-us")
htv := PanelUserListVbs{users}
err := panelUserList.ExecuteTemplate(w, "admin_index.html", htv)
fmt.Println(err)
htv := PanelUserListVbs{users, NewSearchForm(), Navigation{nbUsers, offset, pagenum, "mod_ulist_page"}, currentUser, r.URL}
err = panelUserList.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err)
} else {
http.Error(w, "admins only", http.StatusForbidden)
}
}
func CommentsListPanel(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if userPermission.HasAdmin(currentUser) {
page, _ := strconv.Atoi(r.URL.Query().Get("p"))
offset := 100
vars := mux.Vars(r)
page := vars["page"]
comments := commentService.GetAllComments(offset, page * offset)
var err error
pagenum := 1
if page != "" {
pagenum, err = strconv.Atoi(html.EscapeString(page))
if !log.CheckError(err) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
offset := 100
userid := r.URL.Query().Get("userid")
var conditions string
var values []interface{}
if userid != "" {
conditions = "user_id = ?"
values = append(values, userid)
}
comments, nbComments := commentService.GetAllComments(offset, (pagenum-1)*offset, conditions, values...)
languages.SetTranslationFromRequest(panelCommentList, r, "en-us")
htv := PanelCommentListVbs{comments}
err := panelCommentList.ExecuteTemplate(w, "admin_index.html", htv)
fmt.Println(err)
htv := PanelCommentListVbs{comments, NewSearchForm(), Navigation{nbComments, offset, pagenum, "mod_clist_page"}, currentUser, r.URL}
err = panelCommentList.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err)
} else {
http.Error(w, "admins only", http.StatusForbidden)
}
@ -97,9 +189,9 @@ func TorrentEditModPanel(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
torrent, _ := torrentService.GetTorrentById(id)
languages.SetTranslationFromRequest(panelTorrentEd, r, "en-us")
htv := PanelTorrentEdVbs{torrent}
htv := PanelTorrentEdVbs{torrent, NewSearchForm(), currentUser}
err := panelTorrentEd.ExecuteTemplate(w, "admin_index.html", htv)
fmt.Println(err)
log.CheckError(err)
} else {
http.Error(w, "admins only", http.StatusForbidden)
}
@ -128,7 +220,7 @@ func TorrentPostEditModPanel(w http.ResponseWriter, r *http.Request) {
}
}
languages.SetTranslationFromRequest(panelTorrentEd, r, "en-us")
htv := PanelTorrentEdVbs{torrent}
htv := PanelTorrentEdVbs{torrent, NewSearchForm(), currentUser}
_ = panelTorrentEd.ExecuteTemplate(w, "admin_index.html", htv)
} else {
http.Error(w, "admins only", http.StatusForbidden)

16
router/router.go Fichier normal → Fichier exécutable
Voir le fichier

@ -37,10 +37,12 @@ func init() {
gzipUserLogoutHandler := handlers.CompressHandler(http.HandlerFunc(UserLogoutHandler))
gzipUserProfileHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileHandler))
gzipUserFollowHandler := handlers.CompressHandler(http.HandlerFunc(UserFollowHandler))
gzipUserDetailsHandler := handlers.CompressHandler(http.HandlerFunc(UserDetailsHandler))
gzipUserProfileFormHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileFormHandler))
gzipIndexModPanel := handlers.CompressHandler(http.HandlerFunc(IndexModPanel))
gzipTorrentsListPanel := handlers.CompressHandler(http.HandlerFunc(TorrentsListPanel))
gzipTorrentReportListPanel := handlers.CompressHandler(http.HandlerFunc(TorrentReportListPanel))
gzipUsersListPanel := handlers.CompressHandler(http.HandlerFunc(UsersListPanel))
gzipCommentsListPanel := handlers.CompressHandler(http.HandlerFunc(CommentsListPanel))
gzipTorrentEditModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentEditModPanel))
@ -48,7 +50,6 @@ func init() {
gzipCommentDeleteModPanel := handlers.CompressHandler(http.HandlerFunc(CommentDeleteModPanel))
gzipTorrentDeleteModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentDeleteModPanel))
gzipGetTorrentReportHandler := handlers.CompressHandler(http.HandlerFunc(GetTorrentReportHandler))
//gzipTorrentReportCreateHandler := handlers.CompressHandler(http.HandlerFunc(CreateTorrentReportHandler))
//gzipTorrentReportDeleteHandler := handlers.CompressHandler(http.HandlerFunc(DeleteTorrentReportHandler))
//gzipTorrentDeleteHandler := handlers.CompressHandler(http.HandlerFunc(DeleteTorrentHandler))
@ -81,13 +82,17 @@ func init() {
Router.Handle("/user/logout", gzipUserLogoutHandler).Name("user_logout")
Router.Handle("/user/{id}/{username}", wrapHandler(gzipUserProfileHandler)).Name("user_profile").Methods("GET")
Router.Handle("/user/{id}/{username}/follow", gzipUserFollowHandler).Name("user_follow").Methods("GET")
Router.Handle("/user/{id}/{username}", wrapHandler(gzipUserProfileFormHandler)).Name("user_profile").Methods("POST")
Router.Handle("/user/{id}/{username}/edit", wrapHandler(gzipUserDetailsHandler)).Name("user_profile_details").Methods("GET")
Router.Handle("/user/{id}/{username}/edit", wrapHandler(gzipUserProfileFormHandler)).Name("user_profile_edit").Methods("POST")
Router.Handle("/mod/", gzipIndexModPanel).Name("mod_index")
Router.Handle("/mod", gzipIndexModPanel).Name("mod_index")
Router.Handle("/mod/torrents", gzipTorrentsListPanel).Name("mod_tlist")
Router.Handle("/mod/torrents/{page}", gzipTorrentsListPanel).Name("mod_tlist_page")
Router.Handle("/mod/users", gzipUsersListPanel).Name("mod_ulist")
Router.Handle("/mod/users/{page}", gzipUsersListPanel).Name("mod_ulist_page")
Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_clist")
Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_cedit") // TODO
Router.Handle("/mod/comments/{page}", gzipCommentsListPanel).Name("mod_clist_page")
Router.Handle("/mod/comment", gzipCommentsListPanel).Name("mod_cedit") // TODO
Router.Handle("/mod/torrent/", gzipTorrentEditModPanel).Name("mod_tedit")
Router.Handle("/mod/torrent/", gzipTorrentPostEditModPanel).Name("mod_ptedit")
Router.Handle("/mod/torrent/delete", gzipTorrentDeleteModPanel).Name("mod_tdelete")
@ -102,7 +107,8 @@ func init() {
// TODO Allow only moderators to access /moderation/*
//Router.Handle("/moderation/report/delete", gzipTorrentReportDeleteHandler).Name("torrent_report_delete").Methods("POST")
//Router.Handle("/moderation/torrent/delete", gzipTorrentDeleteHandler).Name("torrent_delete").Methods("POST")
Router.Handle("/moderation/report", gzipGetTorrentReportHandler ).Name("torrent_report").Methods("GET")
Router.Handle("/mod/reports", gzipTorrentReportListPanel).Name("mod_trlist").Methods("GET")
Router.Handle("/mod/reports/{page}", gzipTorrentReportListPanel).Name("mod_trlist_page").Methods("GET")
Router.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
}

Voir le fichier

@ -7,7 +7,7 @@ import (
var TemplateDir = "templates"
var torrentReportTemplate, homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate *template.Template
var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate *template.Template
type templateLoader struct {
templ **template.Template
@ -18,11 +18,6 @@ type templateLoader struct {
// ReloadTemplates reloads templates on runtime
func ReloadTemplates() {
templs := []templateLoader{
templateLoader{
templ: &torrentReportTemplate,
name: "torrent_report",
file: "torrent_report.html",
},
templateLoader{
templ: &homeTemplate,
name: "home",

Voir le fichier

@ -27,9 +27,10 @@ var FuncMap = template.FuncMap{
return "error"
},
"genNav": func(nav Navigation, currentUrl *url.URL, pagesSelectable int) template.HTML {
var ret = ""
if (nav.TotalItem > 0) {
maxPages := math.Ceil(float64(nav.TotalItem) / float64(nav.MaxItemPerPage))
var ret = ""
if nav.CurrentPage-1 > 0 {
url, _ := Router.Get(nav.Route).URL("page", "1")
ret = ret + "<li><a id=\"page-prev\" href=\"" + url.String() + "?" + currentUrl.RawQuery + "\" aria-label=\"Previous\"><span aria-hidden=\"true\">&laquo;</span></a></li>"
@ -57,6 +58,7 @@ var FuncMap = template.FuncMap{
url, _ := Router.Get(nav.Route).URL("page", strconv.Itoa(nav.CurrentPage+1))
ret = ret + "<li><a id=\"page-next\" href=\"" + url.String() + "?" + currentUrl.RawQuery + "\" aria-label=\"Next\"><span aria-hidden=\"true\">&raquo;</span></a></li>"
}
}
return template.HTML(ret)
},
"T": i18n.IdentityTfunc,

Voir le fichier

@ -113,27 +113,51 @@ type UploadTemplateVariables struct {
Route *mux.Route
}
/* MODERATION Variables */
type PanelIndexVbs struct {
Torrents []model.Torrent
Users []model.User
Comments []model.Comment
Torrents []model.Torrent
TorrentReports []model.TorrentReport
Users []model.User
Comments []model.Comment
Search SearchForm
User *model.User
URL *url.URL // For parsing Url in templates
}
type PanelTorrentListVbs struct {
Torrents []model.Torrent
Torrents []model.Torrent
Search SearchForm
Navigation Navigation
User *model.User
URL *url.URL // For parsing Url in templates
}
type PanelUserListVbs struct {
Users []model.User
Users []model.User
Search SearchForm
Navigation Navigation
User *model.User
URL *url.URL // For parsing Url in templates
}
type PanelCommentListVbs struct {
Comments []model.Comment
Comments []model.Comment
Search SearchForm
Navigation Navigation
User *model.User
URL *url.URL // For parsing Url in templates
}
type PanelTorrentEdVbs struct {
Torrent model.Torrent
Search SearchForm
User *model.User
}
type ViewTorrentReportsVariables struct {
Torrents []model.TorrentReportJson
type PanelTorrentReportListVbs struct {
TorrentReports []model.TorrentReportJson
Search SearchForm
Navigation Navigation
User *model.User
URL *url.URL // For parsing Url in templates
}
/*

Voir le fichier

@ -1,12 +1,15 @@
package router
import (
/*import (
"net/http"
"strconv"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service/moderation"
"github.com/ewhal/nyaa/service/user/permission"
)
"github.com/gorilla/mux"
)*/
/*
func SanitizeTorrentReport(torrentReport *model.TorrentReport) {
// TODO unescape html ?
@ -47,24 +50,7 @@ func DeleteTorrentReportHandler(w http.ResponseWriter, r *http.Request) {
}
}
*/
func GetTorrentReportHandler(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if userPermission.HasAdmin(currentUser) {
torrentReports, err := moderationService.GetTorrentReports()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = torrentReportTemplate.ExecuteTemplate(w, "torrent_report.html", ViewTorrentReportsVariables{model.TorrentReportsToJSON(torrentReports)})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
http.Error(w, "admins only", http.StatusForbidden)
}
}
/*
func DeleteTorrentHandler(w http.ResponseWriter, r *http.Request) {
@ -76,3 +62,32 @@ func DeleteTorrentHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}*/
/*func GetTorrentReportHandler(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if userPermission.HasAdmin(currentUser) {
vars := mux.Vars(r)
page, _ := strconv.Atoi(vars["page"])
offset := 100
userid := r.URL.Query().Get("userid")
var conditions string
var values []interface{}
if (userid != "") {
conditions = "user_id = ?"
values = append(values, userid)
}
torrentReports, nbReports, err := moderationService.GetTorrentReports(offset, page * offset, conditions, values...)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = torrentReportTemplate.ExecuteTemplate(w, "admin_index.html", ViewTorrentReportsVariables{model.TorrentReportsToJSON(torrentReports), NewSearchForm(), Navigation{nbReports, offset, page, "mod_trlist_page"}, currentUser, r.URL})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
http.Error(w, "admins only", http.StatusForbidden)
}
}*/

Voir le fichier

@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"github.com/ewhal/nyaa/cache"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/util"
@ -94,6 +95,7 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
f.Name = util.TrimWhitespaces(f.Name)
f.Description = p.Sanitize(util.TrimWhitespaces(f.Description))
f.Magnet = util.TrimWhitespaces(f.Magnet)
cache.Clear()
catsSplit := strings.Split(f.Category, "_")
// need this to prevent out of index panics

47
router/userHandler.go Fichier normal → Fichier exécutable
Voir le fichier

@ -10,6 +10,7 @@ import (
"github.com/ewhal/nyaa/service/user"
"github.com/ewhal/nyaa/service/user/form"
"github.com/ewhal/nyaa/service/user/permission"
"github.com/ewhal/nyaa/util/log"
"github.com/ewhal/nyaa/util/languages"
"github.com/ewhal/nyaa/util/modelHelper"
"github.com/gorilla/mux"
@ -54,30 +55,21 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
userProfile, _, errorUser := userService.RetrieveUserForAdmin(id)
if errorUser == nil {
currentUser := GetUser(r)
view := r.URL.Query()["edit"]
follow := r.URL.Query()["followed"]
unfollow := r.URL.Query()["unfollowed"]
infosForm := form.NewInfos()
deleteVar := r.URL.Query()["delete"]
if (view != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
b := form.UserForm{}
modelHelper.BindValueForm(&b, r)
languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
err := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} else if (deleteVar != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
if (deleteVar != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
err := form.NewErrors()
_, errUser := userService.DeleteUser(w, currentUser, id)
if errUser != nil {
err["errors"] = append(err["errors"], errUser.Error())
}
languages.SetTranslationFromRequest(viewUserDeleteTemplate, r, "en-us")
htv := UserVerifyTemplateVariables{err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
htv := UserVerifyTemplateVariables{err, searchForm, Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
errorTmpl := viewUserDeleteTemplate.ExecuteTemplate(w, "index.html", htv)
if errorTmpl != nil {
http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
@ -90,7 +82,9 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
if unfollow != nil {
infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_unfollowed_msg"), userProfile.Username))
}
htv := UserProfileVariables{&userProfile, infosForm, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
htv := UserProfileVariables{&userProfile, infosForm, searchForm, Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv)
if err != nil {
@ -109,6 +103,25 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
}
}
//Getting User Profile Details View
func UserDetailsHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
b := form.UserForm{}
userProfile, _, errorUser := userService.RetrieveUserForAdmin(id)
if errorUser == nil {
currentUser := GetUser(r)
modelHelper.BindValueForm(&b, r)
languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), searchForm, Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
err := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
// Getting View User Profile Update
func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
@ -129,9 +142,14 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
}
if len(err) == 0 {
modelHelper.BindValueForm(&b, r)
if (!userPermission.HasAdmin(currentUser)) {
b.Username = currentUser.Username
}
err = modelHelper.ValidateForm(&b, err)
log.Info("lol")
if len(err) == 0 {
userProfile, _, errorUser = userService.UpdateUser(w, &b, currentUser, id)
log.Infof("xD2")
if errorUser != nil {
err["errors"] = append(err["errors"], errorUser.Error())
}
@ -169,7 +187,6 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
// Post Registration controller, we do some check on the form here, the rest on user service
func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) {
// Check same Password
b := form.RegistrationForm{}
err := form.NewErrors()
if !captcha.Authenticate(captcha.Extract(r)) {

Voir le fichier

@ -13,6 +13,8 @@ import (
"github.com/ewhal/nyaa/util/languages"
"github.com/ewhal/nyaa/util/log"
"github.com/gorilla/mux"
"fmt"
)
func ViewHandler(w http.ResponseWriter, r *http.Request) {
@ -49,8 +51,8 @@ func PostCommentHandler(w http.ResponseWriter, r *http.Request) {
userID := currentUser.ID
comment := model.Comment{TorrentID: uint(idNum), UserID: userID, Content: content, CreatedAt: time.Now()}
err = db.ORM.Create(&comment).Error
err = db.ORM.Create(&comment).Error
if err != nil {
util.SendError(w, err, 500)
return
@ -75,9 +77,18 @@ func ReportTorrentHandler(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
idNum, err := strconv.Atoi(id)
userID := currentUser.ID
report := model.TorrentReport{Description: r.FormValue("report_type"), TorrentID: uint(idNum), UserID: userID}
torrent, _ := torrentService.GetTorrentById(id)
report := model.TorrentReport{
Description: r.FormValue("report_type"),
TorrentID: uint(idNum),
UserID: userID,
Torrent: torrent,
User: *currentUser,
}
fmt.Println(report)
err = db.ORM.Create(&report).Error
if err != nil {

Voir le fichier

@ -8,7 +8,7 @@ import (
"strings"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service/torrent"
"github.com/ewhal/nyaa/service"
)
type torrentsQuery struct {
@ -40,8 +40,8 @@ type UpdateRequest struct {
Update TorrentRequest `json:"update"`
}
func (r *TorrentsRequest) ToParams() torrentService.WhereParams {
res := torrentService.WhereParams{}
func (r *TorrentsRequest) ToParams() serviceBase.WhereParams {
res := serviceBase.WhereParams{}
conditions := ""
v := reflect.ValueOf(r.Query)

Voir le fichier

@ -6,8 +6,10 @@ import (
)
func GetAllComments(limit int, offset int) []model.Comment{
func GetAllComments(limit int, offset int, conditions string, values ...interface{}) ([]model.Comment, int){
var comments []model.Comment
db.ORM.Limit(limit).Offset(offset).Preload("User").Find(&comments)
return comments
var nbComments int
db.ORM.Model(&comments).Where(conditions, values...).Count(&nbComments)
db.ORM.Preload("User").Limit(limit).Offset(offset).Where(conditions, values...).Find(&comments)
return comments, nbComments
}

Voir le fichier

@ -1,38 +0,0 @@
package moderationService
import (
"errors"
"net/http"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
)
// Return torrentReport in case we did modified it (ie: CreatedAt field)
func CreateTorrentReport(torrentReport model.TorrentReport) (model.TorrentReport, error) {
if db.ORM.Create(&torrentReport).Error != nil {
return torrentReport, errors.New("TorrentReport was not created")
}
return torrentReport, nil
}
func DeleteTorrentReport(id int) (int, error) {
var torrentReport model.TorrentReport
if db.ORM.First(&torrentReport, id).RecordNotFound() {
return http.StatusNotFound, errors.New("Trying to delete a torrent report that does not exists.")
}
if db.ORM.Delete(&torrentReport).Error != nil {
return http.StatusInternalServerError, errors.New("User is not deleted.")
}
return http.StatusOK, nil
}
// TODO Add WhereParams to filter the torrent reports (ie: searching description)
// TODO Use limit, offset
func GetTorrentReports() ([]model.TorrentReport, error) {
var torrentReports []model.TorrentReport
if db.ORM.Preload("User").Preload("Torrent").Find(&torrentReports).Error != nil {
return nil, errors.New("Problem finding all torrent reports.")
}
return torrentReports, nil
}

76
service/report/report.go Fichier normal
Voir le fichier

@ -0,0 +1,76 @@
package reportService
import (
"errors"
"net/http"
"strconv"
"strings"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service"
)
// 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")
}
return nil
}
func DeleteTorrentReport(id int) (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
}
if err := db.ORM.Delete(&torrentReport).Error; err != nil {
return err, http.StatusInternalServerError
}
return nil, http.StatusOK
}
func getTorrentReportsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int, countAll bool) (
torrentReports []model.TorrentReport, count int, err error,
) {
var conditionArray []string
var params []interface{}
if parameters != nil { // if there is where parameters
if len(parameters.Conditions) > 0 {
conditionArray = append(conditionArray, parameters.Conditions)
}
params = parameters.Params
}
conditions := strings.Join(conditionArray, " AND ")
if countAll {
err = db.ORM.Model(&torrentReports).Where(conditions, params...).Count(&count).Error
if err != nil {
return
}
}
// TODO: Vulnerable to injections. Use query builder. (is it?)
// build custom db query for performance reasons
dbQuery := "SELECT * FROM torrent_reports"
if conditions != "" {
dbQuery = dbQuery + " WHERE " + conditions
}
if orderBy == "" { // default OrderBy
orderBy = "torrent_report_id DESC"
}
dbQuery = dbQuery + " ORDER BY " + orderBy
if limit != 0 || offset != 0 { // if limits provided
dbQuery = dbQuery + " LIMIT " + strconv.Itoa(limit) + " OFFSET " + strconv.Itoa(offset)
}
err = db.ORM.Preload("Torrent").Preload("User").Raw(dbQuery, params...).Find(&torrentReports).Error //fixed !!!!
return
}
func GetTorrentReportsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) ([]model.TorrentReport, int, error) {
return getTorrentReportsOrderBy(parameters, orderBy, limit, offset, true)
}
func GetAllTorrentReports(limit int, offset int) ([]model.TorrentReport, int, error) {
return GetTorrentReportsOrderBy(nil, "", limit, offset)
}

17
service/service.go Fichier normal
Voir le fichier

@ -0,0 +1,17 @@
package serviceBase
type WhereParams struct {
Conditions string // Ex : name LIKE ? AND category_id LIKE ?
Params []interface{}
}
func CreateWhereParams(conditions string, params ...string) WhereParams {
whereParams := WhereParams{
Conditions: conditions,
Params: make([]interface{}, len(params)),
}
for i := range params {
whereParams.Params[i] = params[i]
}
return whereParams
}

Voir le fichier

@ -2,21 +2,17 @@ package torrentService
import (
"errors"
"net/http"
"strconv"
"strings"
"net/http"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service"
"github.com/ewhal/nyaa/util"
)
type WhereParams struct {
Conditions string // Ex : name LIKE ? AND category_id LIKE ?
Params []interface{}
}
/* Function to interact with Models
*
* Get the torrents with where clause
@ -51,7 +47,8 @@ func GetFeeds() (result []model.Feed, err error) {
}
func GetTorrentById(id string) (torrent model.Torrent, err error) {
id_int, err := strconv.Atoi(id)
// Postgres DB integer size is 32-bit
id_int, err := strconv.ParseInt(id, 10, 32)
if err != nil {
return
}
@ -73,6 +70,13 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
// (or maybe I'm just retarded)
torrent.Uploader = new(model.User)
db.ORM.Where("user_id = ?", torrent.UploaderID).Find(torrent.Uploader)
torrent.OldUploader = ""
if torrent.ID <= config.LastOldTorrentID {
var tmp model.UserUploadsOld
if !db.ORM.Where("torrent_id = ?", torrent.ID).Find(&tmp).RecordNotFound() {
torrent.OldUploader = tmp.Username
}
}
for i := range torrent.Comments {
torrent.Comments[i].User = new(model.User)
err = db.ORM.Where("user_id = ?", torrent.Comments[i].UserID).Find(torrent.Comments[i].User).Error
@ -84,21 +88,21 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
return
}
func GetTorrentsOrderByNoCount(parameters *WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, err error) {
func GetTorrentsOrderByNoCount(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, err error) {
torrents, _, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, false)
return
}
func GetTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, count int, err error) {
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)
return
}
func getTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offset int, countAll bool) (
func getTorrentsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int, countAll bool) (
torrents []model.Torrent, count int, err error,
) {
var conditionArray []string
conditionArray = append(conditionArray, "deleted_at IS NULL")
conditionArray = append(conditionArray, "deleted_at IS NULL")
if strings.HasPrefix(orderBy, "filesize") {
// torrents w/ NULL filesize fuck up the sorting on Postgres
conditionArray = append(conditionArray, "filesize IS NOT NULL")
@ -112,12 +116,13 @@ func getTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offs
}
conditions := strings.Join(conditionArray, " AND ")
if countAll {
// FIXME: `deleted_at IS NULL` is duplicate in here because GORM handles this for us
err = db.ORM.Model(&torrents).Where(conditions, params...).Count(&count).Error
if err != nil {
return
}
}
// TODO: Vulnerable to injections. Use query builder.
// TODO: Vulnerable to injections. Use query builder. (is it?)
// build custom db query for performance reasons
dbQuery := "SELECT * FROM torrents"
@ -143,12 +148,12 @@ func getTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offs
// database. The list will be of length 'limit' and in default order.
// GetTorrents returns the first records found. Later records may be retrieved
// by providing a positive 'offset'
func GetTorrents(parameters WhereParams, limit int, offset int) ([]model.Torrent, int, error) {
func GetTorrents(parameters serviceBase.WhereParams, limit int, offset int) ([]model.Torrent, int, error) {
return GetTorrentsOrderBy(&parameters, "", limit, offset)
}
// Get Torrents with where parameters but no limit and order by default (get all the torrents corresponding in the db)
func GetTorrentsDB(parameters WhereParams) ([]model.Torrent, int, error) {
func GetTorrentsDB(parameters serviceBase.WhereParams) ([]model.Torrent, int, error) {
return GetTorrentsOrderBy(&parameters, "", 0, 0)
}
@ -164,17 +169,6 @@ func GetAllTorrentsDB() ([]model.Torrent, int, error) {
return GetTorrentsOrderBy(nil, "", 0, 0)
}
func CreateWhereParams(conditions string, params ...string) WhereParams {
whereParams := WhereParams{
Conditions: conditions,
Params: make([]interface{}, len(params)),
}
for i := range params {
whereParams.Params[i] = params[i]
}
return whereParams
}
func DeleteTorrent(id string) (int, error) {
var torrent model.Torrent
if db.ORM.First(&torrent, id).RecordNotFound() {

Voir le fichier

@ -116,6 +116,9 @@ func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, er
// RegisterHanderFromForm sets cookie from a RegistrationForm.
func RegisterHanderFromForm(w http.ResponseWriter, registrationForm formStruct.RegistrationForm) (int, error) {
email := registrationForm.Email
if email == "" {
email = registrationForm.Username
}
pass := registrationForm.Password
log.Debugf("RegisterHandler UserEmail : %s", email)
log.Debugf("RegisterHandler UserPassword : %s", pass)

Voir le fichier

@ -49,7 +49,7 @@ func IsAgreed(termsAndConditions string) bool { // TODO: Inline function
// RegistrationForm is used when creating a user.
type RegistrationForm struct {
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
Email string `form:"email" needed:"true"`
Email string `form:"email"`
Password string `form:"password" needed:"true" len_min:"6" len_max:"25" equalInput:"ConfirmPassword"`
ConfirmPassword string `form:"password_confirmation" omit:"true" needed:"true"`
CaptchaID string `form:"captchaID" omit:"true" needed:"true"`
@ -64,13 +64,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" needed:"true"`
Language string `form:"language" default:"en-us"`
CurrentPassword string `form:"password" len_min:"6" len_max:"25" omit:"true"`
Password string `form:"password" len_min:"6" len_max:"25" equalInput:"ConfirmPassword"`
ConfirmPassword string `form:"password_confirmation" omit:"true"`
Status int `form:"language" default:"0"`
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
Email string `form:"email" needed:"true"`
Language string `form:"language" default:"en-us"`
CurrentPassword string `form:"current_password" len_min:"6" len_max:"25" omit:"true"`
Password string `form:"password" len_min:"6" len_max:"25" equalInput:"Confirm_Password"`
Confirm_Password string `form:"password_confirmation" omit:"true"`
Status int `form:"status" default:"0"`
}
// PasswordForm is used when updating a user password.

Voir le fichier

@ -20,7 +20,7 @@ func CurrentOrAdmin(user *model.User, userID uint) bool {
// CurrentUserIdentical check that userID is same as current user's ID.
// TODO: Inline this
func CurrentUserIdentical(user *model.User, userID uint) bool {
return user.ID != userID
return user.ID == userID
}
func GetRole(user *model.User) string {

Voir le fichier

@ -173,7 +173,7 @@ func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *m
if db.ORM.First(&user, id).RecordNotFound() {
return user, http.StatusNotFound, errors.New("user not found")
}
log.Infof("updateUser")
if form.Password != "" {
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(form.CurrentPassword))
if err != nil && !userPermission.HasAdmin(currentUser) {
@ -273,10 +273,12 @@ func RetrieveUserForAdmin(id string) (model.User, int, error) {
}
// RetrieveUsersForAdmin retrieves users for an administrator.
func RetrieveUsersForAdmin(limit int, offset int) []model.User {
func RetrieveUsersForAdmin(limit int, offset int) ([]model.User, int) {
var users []model.User
db.ORM.Preload("Torrents").Find(&users).Limit(limit).Offset(offset)
return users
var nbUsers int
db.ORM.Model(&users).Count(&nbUsers)
db.ORM.Preload("Torrents").Limit(limit).Offset(offset).Find(&users)
return users, nbUsers
}
// CreateUserAuthentication creates user authentication.

Voir le fichier

@ -7,6 +7,7 @@
<img src="https://www.gravatar.com/avatar/{{ .MD5 }}?s=50" class="img-circle special-img"> {{ .Username }} <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}"><i class="fa fa-cog"></i> Profile</a></li>
{{if HasAdmin . }}<li><a href="{{ genRoute "mod_index" }}"><i class="fa fa-cog"></i> {{T "moderation"}}</a></li>{{end}}
<li class="divider"></li>
<li><a href="{{ genRoute "user_logout" }}"><i class="fa fa-sign-out"></i> {{ T "sign_out"}}</a></li>
</ul>

Voir le fichier

@ -10,7 +10,7 @@
<form class="form-horizontal" role="form" method="POST">
<div class="form-group">
<label class="col-lg-3 control-label">{{ T "email" }}:</label>
<label class="col-lg-3 control-label">{{ T "email_address" }}:</label>
<div class="col-lg-8">
<input class="form-control" type="text" name="email" id="email" value="{{.Email}}">
{{ range (index $.FormErrors "email")}}

Voir le fichier

@ -11,7 +11,7 @@
<th>{{T "links"}}</th>
</tr>
{{ range .Torrents }}
{{ with .ToJson }}
{{ with .ToJSON }}
<tr class="torrent-info
{{if eq .Status 2}}remake{{end}}
{{if eq .Status 3}}trusted{{end}}

Voir le fichier

@ -1,8 +1,15 @@
{{define "title"}}Comments List{{end}}
{{define "content"}}
<table>
<table class="table">
{{ range .Comments}}
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cdelete" }}?id={{ .ID }}">Delete</a></td></tr>
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .User.Username }}</a></td>
<td><a href="{{ genRoute "mod_cdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
{{ genNav .Navigation .URL 5 }}
</ul>
</nav>
{{end}}

Voir le fichier

@ -1,7 +1,57 @@
{{define "title"}}Moderation Overview{{end}}
{{define "content"}}
nigga this just be some links
<a href="/mod/torrents">torrent list</a>
<a href="/mod/users">user list </a>
<a href="/mod/comments">comments list</a>
<h3 id="torrents">Last Torrents</h3>
<table class="table">
{{ range .Torrents}}
{{end}}
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Name }}</a></td><td><a href="{{ genRoute "mod_tlist" }}?userid={{.UploaderID}}">{{ .UploaderID }}</a></td>
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
<li><a href="{{ genRoute "mod_tlist" }}">More</a></li>
</ul>
</nav>
<h3 id="torrents">Last Torrents Report</h3>
<table class="table">
{{ range .TorrentReports}}
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.User.Username}}</td><td>{{.Description}}</td>
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
<li><a href="{{ genRoute "mod_trlist" }}">More</a></li>
</ul>
</nav>
<h3 id="users">Last Users</h3>
<table class="table">
{{ range .Users}}
<tr>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?edit">{{ .Username }}</a></td>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?delete" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td>
</tr>
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
<li><a href="{{ genRoute "mod_ulist" }}">More </a></li>
</ul>
</nav>
<h3 id="comments">Last Comments</h3>
<table class="table">
{{ range .Comments}}
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .User.Username }}</a></td>
<td><a href="{{ genRoute "mod_cdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
<li><a href="{{ genRoute "mod_clist" }}">More</a></li>
</ul>
</nav>
{{end}}

Voir le fichier

@ -0,0 +1,8 @@
{{define "title"}}Torrents Report{{end}}
{{define "content"}}
<table class="table">
{{ range .TorrentReports}}
<tr><td><a href="{{ genRoute "mod_tedit"}}?id={{.Torrent.ID}}">{{.Torrent.Name}}</a></td><td>{{.User.Username}}</td><td>{{.Description}}</td><td><a href="{{ genRoute "mod_tdelete" }}?id={{ .Torrent.ID }}">Delete</a></td></tr>
{{end}}
</table>
{{end}}

Voir le fichier

@ -1,8 +1,15 @@
{{define "title"}}Torrents List{{end}}
{{define "content"}}
<table>
<table class="table">
{{ range .Torrents}}
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Name }}</a></td><td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}">Delete</a></td></tr>
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Name }}</a></td><td><a href="{{ genRoute "mod_tlist" }}?userid={{.UploaderID}}">{{ .UploaderID }}</a></td>
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
{{ genNav .Navigation .URL 5 }}
</ul>
</nav>
{{end}}

Voir le fichier

@ -1,8 +1,17 @@
{{define "title"}}Users List{{end}}
{{define "content"}}
<table>
<table class="table">
{{ range .Users}}
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Username }}</a></td><td><a href="{{ genRoute "mod_tdelete" }}?id{{ .ID }}">Delete</a></td></tr>
<tr>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?edit">{{ .Username }}</a></td>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?delete" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td>
</tr>
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
{{ genNav .Navigation .URL 5 }}
</ul>
</nav>
{{end}}

Voir le fichier

@ -27,10 +27,46 @@
<!-- Website CSS -->
</head>
<body>
<nav class="navbar navbar-default" id="mainmenu">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">{{ T "toggle_navigation" }}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{.URL.Parse "/"}}">Nyaa Pantsu Moderation</a>
<a class="navbar-brand hidden-md pull-right nightswitch" href="javascript:toggleNightMode();" ></a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{{.URL.Parse "/"}}">{{T "website"}}</a/></li>
<li><a href="{{ genRoute "mod_tlist"}}">{{T "torrents"}}</a></li>
<li><a href="{{ genRoute "mod_ulist"}}">{{T "users"}}</a></li>
<li><a href="{{ genRoute "mod_clist"}}">{{T "comments"}}</a></li>
<li><a href="{{ genRoute "mod_trlist"}}">{{T "torrent_reports"}}</a></li>
</ul>
{{block "badge_user" .}}{{end}}
<form class="navbar-form navbar-right" role="search" action="/mod/torrents" method="get">
<div class="form-group">
{{block "search_common" .}}{{end}}
</div>
<div class="form-group">
{{block "search_button" .}}{{end}}
</div>
</form>
</div>
</div>
</nav>
<div class="container" id="container">
{{block "content" .}}{{end}}
</div>
<footer style="text-align: center; padding-bottom: 2rem;font-size: 2rem;font-family: cursive; color: #616161;text-shadow: -1px -1px #999999;">
Powered by NyaaPantsu
</footer>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->

Voir le fichier

@ -34,8 +34,8 @@
{{.Name}}
</a>
</td>
<td class="hidden-xs" class="date date-short">{{.Date}}</td>
<td class="hidden-xs" class="filesize">{{.Filesize}}</td>
<td class="hidden-xs date date-short">{{.Date}}</td>
<td class="hidden-xs filesize">{{.Filesize}}</td>
<td class="hidden-xs">
<a href="{{.Magnet}}" title="Magnet link">
<span class="glyphicon glyphicon-magnet" aria-hidden="true"></span>

Voir le fichier

@ -1,8 +0,0 @@
<!DOCTYPE html>
<html>
<table>
{{ range .Torrents}}
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.User}}</td><td>{{.Description}}</td><td><a href="{{ genRoute "mod_tdelete" }}?id={{ .Torrent.ID }}">Delete</a></td></tr>
{{end}}
</table>
</html>

23
templates/user/profile.html Fichier normal → Fichier exécutable
Voir le fichier

@ -26,13 +26,13 @@
<!-- SIDEBAR BUTTONS -->
<div class="profile-userbuttons">
{{if gt $.User.ID 0 }}
{{if not (CurrentUserIdentical $.User .ID) }}
{{if not (IsFollower . $.User)}}
<a href="{{ genRoute "user_follow" "id" ( print .ID ) "username" .Username }}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
{{else}}
<a href="{{ genRoute "user_follow" "id" ( print .ID ) "username" .Username }}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
{{end}}
{{end}}
{{if not (CurrentUserIdentical $.User .ID) }}
{{if not (IsFollower . $.User)}}
<a href="{{ genRoute "user_follow" "id" ( print .ID ) "username" .Username }}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
{{else}}
<a href="{{ genRoute "user_follow" "id" ( print .ID ) "username" .Username }}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
{{end}}
{{end}}
{{end}}
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
</div>
@ -41,16 +41,13 @@
<div class="profile-usermenu">
<ul class="nav">
<li class="active">
<a href="#">
<i class="glyphicon glyphicon-home"></i>
{{T "torrents"}} </a>
<a href="{{ genRoute "user_profile" "id" ( print .ID ) "username" .Username }}"><i class="glyphicon glyphicon-home"></i>{{T "torrents"}}</a>
</li>
{{if gt $.User.ID 0 }}
{{if CurrentOrAdmin $.User .ID }}
<li>
<a href="?edit">
<i class="glyphicon glyphicon-user"></i>
{{T "settings"}} </a>
<a href="{{ genRoute "user_profile_details" "id" ( print .ID ) "username" .Username }}"><i class="glyphicon glyphicon-user"></i>{{T "settings"}}</a>
</li>
{{end}}
{{end}}

30
templates/user/profile_edit.html Fichier normal → Fichier exécutable
Voir le fichier

@ -23,13 +23,13 @@
<!-- SIDEBAR BUTTONS -->
<div class="profile-userbuttons">
{{if gt $.User.ID 0 }}
{{if not (CurrentUserIdentical $.User .ID) }}
{{if not (IsFollower . $.User)}}
<a href="{{ genRoute "user_follow" "id" (print .ID)}}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
{{else}}
<a href="{{ genRoute "user_follow" "id" (print .ID)}}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
{{end}}
{{end}}
{{if not (CurrentUserIdentical $.User .ID) }}
{{if not (IsFollower . $.User)}}
<a href="{{ genRoute "user_follow" "id" (print .ID) "username" .Username }}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
{{else}}
<a href="{{ genRoute "user_follow" "id" (print .ID) "username" .Username }}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
{{end}}
{{end}}
{{end}}
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
</div>
@ -38,18 +38,14 @@
<div class="profile-usermenu">
<ul class="nav">
<li>
<a href="#">
<i class="glyphicon glyphicon-home"></i>
{{T "torrents"}} </a>
<a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}"><i class="glyphicon glyphicon-home"></i>{{T "torrents"}}</a>
</li>
{{if gt $.User.ID 0 }}
{{if CurrentOrAdmin $.User .ID }}
<li class="active">
<a href="?edit">
<i class="glyphicon glyphicon-user"></i>
{{T "settings"}} </a>
</li>
{{end}}
{{if CurrentOrAdmin $.User .ID }}
<li class="active">
<a href="{{ genRoute "user_profile_edit" "id" (print .ID) "username" .Username }}"><i class="glyphicon glyphicon-user"></i>{{T "settings"}}</a>
</li>
{{end}}
{{end}}
</ul>
</div>

Voir le fichier

@ -28,7 +28,12 @@
</tr>
<tr>
<td>Uploader</td>
<td><a href="{{$.URL.Parse (printf "/user/%d/-" .UploaderID) }}">{{.UploaderName}}</a></td>
<td>
<a href="{{$.URL.Parse (printf "/user/%d/-" .UploaderID) }}">{{.UploaderName}}</a>
{{if ne .OldUploader ""}}
({{.OldUploader}})
{{end}}
</td>
{{if ne .WebsiteLink ""}}
<tr>
<td>{{T "Link"}}</td>

566
translations/de-de.all.json Fichier normal
Voir le fichier

@ -0,0 +1,566 @@
[
{
"id": "link",
"translation": "Link"
},
{
"id": "verify_email_title",
"translation": "Bestätige deine E-Mail Adresse für Nyaapantsu."
},
{
"id": "verify_email_content",
"translation": "Klicke unten auf den Link um deine E-Mail Adresse zu verifizieren."
},
{
"id": "reset_password_title",
"translation": "Setze dein nyaapantsu Passwort zurück."
},
{
"id": "reset_password_content",
"translation": "Klicke unten auf den Link um dein Passwort zurückzusetzen."
},
{
"id":"register_title",
"translation": "Neuen Account erstellen"
},
{
"id":"signup_box_title",
"translation": "Registriere dich <small>Es ist kostenlos und wird immer so bleiben.</small>"
},
{
"id":"username",
"translation": "Nutzername"
},
{
"id":"email_address_or_username",
"translation": "E-Mail-Adresse oder Nutzername"
},
{
"id":"email_address",
"translation": "E-Mail Adresse"
},
{
"id":"password",
"translation": "Passwort"
},
{
"id":"confirm_password",
"translation": "Bestätige dein Passwort"
},
{
"id":"i_agree",
"translation": "Ich stimme zu"
},
{
"id":"terms_conditions_confirm",
"translation": "Mit Klick auf <strong class=\"label label-primary\">Registrieren</strong> stimmst du den <a href=\"#\" data-toggle=\"modal\" data-target=\"#t_and_c_m\">Nutzungsbedingungen</a> sowie der Nutzung von Cookies zu."
},
{
"id":"signin",
"translation": "Einloggen"
},
{
"id":"register",
"translation": "Registrieren"
},
{
"id":"terms_conditions",
"translation": "Nutzungsbedingungen"
},
{
"id":"terms_conditions_full",
"translation": "Coming sooner than you might expect..."
},
{
"id":"remember_me",
"translation": "Login speichern"
},
{
"id":"forgot_password",
"translation": "Passwort vergessen?"
},
{
"id":"sign_in_box_title",
"translation": "Bitte logge dich ein"
},
{
"id":"sign_in_title",
"translation": "Login"
},
{
"id":"register_success_title",
"translation": "Login erfolgreich"
},
{
"id":"sign_up_success",
"translation": "Danke für's Registrieren!"
},
{
"id":"verify_success",
"translation": "<i style=\"color:limegreen\" class=\"glyphicon glyphicon-ok-circle\"></i>Dein Account ist jetzt aktiviert!"
},
{
"id":"signup_verification_email",
"translation": "Geschafft, überprüfe deinen Posteingang (und Spam Ordner!) auf eine Bestätigungsmail."
},
{
"id":"signup_verification_noemail",
"translation": "Die Registrierung war erfolgreich, du kannst deinen Account jetzt benutzen."
},
{
"id":"settings",
"translation": "Profil-Einstellungen"
},
{
"id":"torrents",
"translation": "Torrents"
},
{
"id":"follow",
"translation": "Folgen"
},
{
"id":"unfollow",
"translation": "Nicht mehr Folgen"
},
{
"id":"user_followed_msg",
"translation": "Du folgst jetzt %s!"
},
{
"id":"user_unfollowed_msg",
"translation": "Du folgst %s nicht mehr!"
},
{
"id":"profile_page",
"translation": "Profil von %s"
},
{
"id":"see_more_torrents_from",
"translation": "Mehr Torrents von %s "
},
{
"id":"category",
"translation": "Kategorie"
},
{
"id": "name",
"translation": "Name"
},
{
"id": "date",
"translation": "Datum"
},
{
"id": "size",
"translation": "Größe"
},
{
"id": "links",
"translation": "Links"
},
{
"id": "home",
"translation": "Home"
},
{
"id": "error_404",
"translation": "Fehler 404"
},
{
"id": "toggle_navigation",
"translation": "Navigation umschalten"
},
{
"id": "upload",
"translation": "Hochladen"
},
{
"id": "faq",
"translation": "FAQ"
},
{
"id": "fap",
"translation": "Fap"
},
{
"id": "advanced_search",
"translation": "Erweiterte Suche"
},
{
"id": "nothing_here",
"translation": "Nichts zu finden."
},
{
"id": "404_not_found",
"translation": "404 nichts gefunden"
},
{
"id": "no_torrents_uploaded",
"translation": "Es sind noch keine Torrents hochgeladen worden!"
},
{
"id": "profile",
"translation": "Profil"
},
{
"id": "sign_out",
"translation": "Logout"
},
{
"id": "member",
"translation": "Mitglied"
},
{
"id": "sign_in",
"translation": "Einloggen"
},
{
"id": "sign_up",
"translation": "Registrieren"
},
{
"id": "no_results_found",
"translation": "Es wurden keine Ergebnisse gefunden"
},
{
"id": "notice_keep_seeding",
"translation": "WICHTIG: WEITER SEEDEN UND DHT AKTIVIEREN"
},
{
"id": "official_nyaapocalipse_faq",
"translation": "Offizielles Nyaapocalypse FAQ"
},
{
"id": "links_replacement_mirror",
"translation": "Ersatzlinks"
},
{
"id": "what_happened",
"translation": "Was ist passiert?"
},
{
"id": "nyaa_se_went_offline",
"translation": "nyaa.se und andere Domains (wie nyaatorrents.info) gingen am 01. Mai 2017 offline."
},
{
"id": "its_not_a_ddos",
"translation": "Die Seite wurde deaktiviert, es war kein DDoS Angriff."
},
{
"id": "future_not_looking_good",
"translation": "Die Zukunft für nyaa sieht schlecht aus. (Die Seite ist tot)"
},
{
"id": "recovery_effort",
"translation": "Zur Zeit wird versucht die Seite zu ersetzen."
},
{
"id": "is_everything_lost",
"translation": "Ist alles verloren?"
},
{
"id": "in_short_no",
"translation": "Kurzgesagt, Nein."
},
{
"id": "are_some_things_lost",
"translation": "Gibt es Verluste?"
},
{
"id": "answer_is_nyaa_db_lost",
"translation": "Wir haben die Torrent-Datenbank von nyaa bis zum <s>5. April</s> 1. Mai. Das heißt es fehlt fast gar nichts."
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "Um Sukebei steht es hingegen schlechter. Zurzeit haben wir nur eine Sukebei-Datenbank bis 2016, aber eine neuere Datenbank steht möglicherweise zu Verfügung."
},
{
"id": "how_are_we_recovering",
"translation": "Wie läuft die Wiederherstellung ab?"
},
{
"id": "answer_how_are_we_recovering",
"translation": "Die obengenannten Datenbanken werden im Moment auf nyaa.pantsu.cat und sukebei.pantsu.cat bereitgestellt. Es gibt eine Suchfunktion und (fast) vollständige Funktionalität von nyaa sollte bald wiederhergestellt sein. Seeder/Leecher Statistiken sind via Scraping möglich und werden in Zukunft vielleicht wiederhergestellt werden, da andere Funktionen Vorrang haben."
},
{
"id": "are_the_trackers_working",
"translation": "Funktionieren die Torrents noch?"
},
{
"id": "answer_are_the_trackers_working",
"translation": "Auch went die Tracker offline sind, sind die Seeder immernoch mit dem dezentralisierten DHT Netzwerk verbunden. Solange das DHT Netzwerk die Torrentdaten hat, sollte es wie gewohnt weitergehen."
},
{
"id": "how_do_i_download_the_torrents",
"translation": "Wie lade ich Torrents herunter?"
},
{
"id": "answer_how_do_i_download_the_torrents",
"translation": "Benutze einfach <b>Magnet Links</b>. Deine BitTorrent Software liest den Magnet Link und durchsucht das DHT Netzwerk nach den Metadaten und der Download sollte ohne weiteres funktionieren."
},
{
"id": "magnet_link_should_look_like",
"translation": "Ein Magnet Link sollte so aussehen:"
},
{
"id": "which_trackers_do_you_recommend",
"translation": "Welche Tracker sind empfohlen?"
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "Wenn deine Torrents wegen Trackern Probleme machen, solltest du einige dieser hinzufügen:"
},
{
"id": "how_can_i_help",
"translation": "Wie kann ich helfen?"
},
{
"id": "answer_how_can_i_help",
"translation": "Wenn du Erfahrungen mit Webseitenprogrammierung hast, kannst du dem IRC-Kanal #nyaapantsu auf irc.rizon.net(Englisch) beitreten. Wenn du irgendwelche Datenbanken hast, insbesondere für Sukebei, <b>LAD SIE HOCH</b>."
},
{
"id": "your_design_sucks_found_a_bug",
"translation": "Euer Design ist mies / Ich habe einen Fehler gefunden"
},
{
"id": "why_written_in_go",
"translation": "Warum in Gottesnamen ist eurer Code in Go geschrieben?"
},
{
"id": "authors_favorite_language",
"translation": "Weil es die Lieblingsprogrammiersprache des Entwicklers ist."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat und sukebei.pantsu.cat stellen keine Dateien bereit."
},
{
"id": "upload_magnet",
"translation": "Magnet hochladen"
},
{
"id": "torrent_file",
"translation": "Torrent Datei"
},
{
"id": "uploading_file_prefills_fields",
"translation": "Beim hochladen einer Torrentdatei werden einige Felder automatisch ausgefüllt (wird bevorzugt)."
},
{
"id": "magnet_link",
"translation": "Magnet Link"
},
{
"id": "all_categories",
"translation": "Alle Kategorien"
},
{
"id": "anime",
"translation": "Anime"
},
{
"id": "anime_amv",
"translation": "Anime - Anime Music Video"
},
{
"id": "anime_english_translated",
"translation": "Anime - Englisch-übersetzt"
},
{
"id": "anime_non_english_translated",
"translation": "Anime - andere Übersetzungen"
},
{
"id": "anime_raw",
"translation": "Anime - Raw"
},
{
"id": "audio",
"translation": "Audio"
},
{
"id": "audio_lossless",
"translation": "Audio - Verlustfrei"
},
{
"id": "audio_lossy",
"translation": "Audio - Verlustbehaftet"
},
{
"id": "literature",
"translation": "Literatur"
},
{
"id": "literature_english_translated",
"translation": "Literatur - Englisch-übersetzt"
},
{
"id": "literature_raw",
"translation": "Literatur - Raw"
},
{
"id": "literature_non_english_translated",
"translation": "Literature - andere Übersetzungen"
},
{
"id": "live_action",
"translation": "Live Action"
},
{
"id": "live_action_english_translated",
"translation": "Live Action - Englisch-übersetzt"
},
{
"id": "live_action_idol_pv",
"translation": "Live Action - Idol/Promotional Video"
},
{
"id": "live_action_non_english_translated",
"translation": "Live Action - andere Übersetzungen"
},
{
"id": "live_action_raw",
"translation": "Live Action - Raw"
},
{
"id": "pictures",
"translation": "Bilder"
},
{
"id": "pictures_graphics",
"translation": "Bilder - Grafiken"
},
{
"id": "pictures_photos",
"translation": "Bilder - Fotos"
},
{
"id": "software",
"translation": "Software"
},
{
"id": "software_applications",
"translation": "Software - Programme"
},
{
"id": "software_games",
"translation": "Software - Spiele"
},
{
"id": "torrent_description",
"translation": "Torrent Beschreibung"
},
{
"id": "description_markdown_notice",
"translation": "Markdown kann in der Beschreibung verwendet werden."
},
{
"id": "show_all",
"translation": "Alle anzeigen"
},
{
"id": "filter_remakes",
"translation": "Remakes herausfiltern"
},
{
"id": "trusted",
"translation": "Trusted"
},
{
"id": "id",
"translation": "ID"
},
{
"id": "downloads",
"translation": "Downloads"
},
{
"id": "descending",
"translation": "Absteigend"
},
{
"id": "ascending",
"translation": "Aufsteigend"
},
{
"id": "search",
"translation": "Suche"
},
{
"id": "hash",
"translation": "Hash"
},
{
"id": "description",
"translation": "Beschreibung"
},
{
"id": "comments",
"translation": "Kommentare"
},
{
"id": "submit_a_comment_as_username",
"translation": "Kommentar als %s verfassen"
},
{
"id": "submit_a_comment_as_anonymous",
"translation": "anonymen Kommentar verfassen"
},
{
"id": "submit",
"translation": "Absenden"
},
{
"id": "personal_info",
"translation": "Persönliche Informationen"
},
{
"id": "language",
"translation": "Sprache"
},
{
"id": "current_password",
"translation": "Derzeitiges Passwort"
},
{
"id": "role",
"translation": "Rolle"
},
{
"id": "banned",
"translation": "Gebannt"
},
{
"id": "default",
"translation": "Standard"
},
{
"id": "trusted_member",
"translation": "Trusted"
},
{
"id": "moderator",
"translation": "Moderator"
},
{
"id": "save_changes",
"translation": "Änderungen speichern"
},
{
"id": "profile_updated",
"translation": "Dein Profil wurde entsprechend geändert!"
},
{
"id": "delete_account",
"translation": "Profil löschen"
},
{
"id": "delete_account_confirm",
"translation": "Bist du sicher das du dein Profil löschen möchtest?"
},
{
"id": "delete_success",
"translation": "Dein Profil wurde erfolgreich gelöscht!"
}
]

Voir le fichier

@ -328,7 +328,7 @@
"translation": "Archivo Torrent"
},
{
"id": "uploading_torrent_prefills_fields",
"id": "uploading_file_prefills_fields",
"translation": "Subir un archivo torrent permite pre-llenar algunos campos, esto es recomendado."
},
{

Voir le fichier

@ -117,7 +117,19 @@
},
{
"id":"follow",
"translation": "Suivre"
"translation": "S'abonner"
},
{
"id":"unfollow",
"translation": "Se désabonner"
},
{
"id":"user_followed_msg",
"translation": "Vous vous êtes abonné à %s!"
},
{
"id":"user_unfollowed_msg",
"translation": "Vous vous êtes désabonné de %s!"
},
{
"id":"profile_page",
@ -213,15 +225,15 @@
},
{
"id": "notice_keep_seeding",
"translation": "IMPORTANT : Continuez le partage et activez le DHT."
"translation": "IMPORTANT : Continuez le partage et activez DHT sur votre client BitTorrent."
},
{
"id": "official_nyaapocalipse_faq",
"translation": "FAQ Officielle du Nyaapocalypse"
"translation": "FAQ Officielle de la Nyaapocalypse"
},
{
"id": "links_replacement_mirror",
"translation": "Liens pour le remplacement/miroir"
"translation": "Liens miroir"
},
{
"id": "what_happened",
@ -233,15 +245,15 @@
},
{
"id": "its_not_a_ddos",
"translation": "Ils ont été désactivés, ce n'est pas une attaque DDoS comme d'habitude."
"translation": "Ils ont été désactivés et soumis à suppression, ce n'est pas une attaque DDoS comme d'habitude."
},
{
"id": "future_not_looking_good",
"translation": "Les persectives d'avenir pour Nyaa ne sont pas bonnes. (C'est mort)"
"translation": "Nyaa est vraisemblablement mort pour de bon."
},
{
"id": "recovery_effort",
"translation": "Un travail de récupération se déroule actuellement."
"translation": "Un travail de récupération est en cours."
},
{
"id": "is_everything_lost",
@ -253,23 +265,23 @@
},
{
"id": "are_some_things_lost",
"translation": "Certaines choses sont-elles perdues ?"
"translation": "Qu'est-ce qui a pu être récupéré ?"
},
{
"id": "answer_is_nyaa_db_lost",
"translation": "Nous avons une base de données des torrents datant du <s>5 Avril</s> 1er Mai. Ce qui signifie que presque rien n'est perdu."
"translation": "Nous avons une base de données des torrents de Nyaa datant du <s>5 Avril</s> 1er Mai. Ce qui signifie que presque rien n'a été perdu."
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "Sukebei, cependant, semble en plus mauvais état. Actuellement nous avons seulement des bases de données datant de 2016, mais une base de donnée plus récente pourrait être disponible à l'usage."
"translation": "Sukebei, cependant, semble en plus mauvais état. La base de donnée actuelle remonte à 2016, mais une version plus récente pourrait être disponible à l'usage."
},
{
"id": "how_are_we_recovering",
"translation": "Comment récupérons-nous ?"
"translation": "Où en est le développement ?"
},
{
"id": "answer_how_are_we_recovering",
"translation": "Les bases de données mentionnées sont hébergées sur nyaa.pantsu.cat et sukebei.pantsu.cat. Une fonction de recherche est disponible et (presque) toutes les fonctionnalités de Nyaa devraient arriver. Les statistiques Seeders/Leechers peuvent être obtenues par 'scraping' et pourraient être restaurées à l'avenir, toutefois d'autres fonctionnalités prennent la priorité en ce moment."
"translation": "Les bases de données mentionnées sont hébergées sur nyaa.pantsu.cat et sukebei.pantsu.cat. Une fonction de recherche est disponible et (presque) toutes les fonctionnalités de Nyaa devraient arriver. Les statistiques seeders/leechers peuvent être obtenues par 'scraping' et pourraient être restaurées à l'avenir, toutefois d'autres fonctionnalités prennent la priorité en ce moment."
},
{
"id": "are_the_trackers_working",
@ -317,7 +329,7 @@
},
{
"id": "authors_favorite_language",
"translation": "C'est le langage de programmation favori de l'auteur."
"translation": "C'est le langage de programmation favori du développeur."
},
{
"id": "nyaa_pantsu_dont_host_files",
@ -333,7 +345,7 @@
},
{
"id": "uploading_file_prefills_fields",
"translation": "Uploader un fichier torrent permet de pré-remplir certains champs, ce qui est recommandé."
"translation": "Il est recommandé d'uploader un fichier torrent afin de pré-remplir certains champs."
},
{
"id": "magnet_link",
@ -503,13 +515,17 @@
"id": "personal_info",
"translation": "Informations personnelles"
},
{
"id": "email",
"translation": "Adresse email"
},
{
"id": "language",
"translation": "Langue"
},
{
"id": "current_password",
"translation": "Mot de passe actuel"
"translation": "Mot de passe actuel "
},
{
"id": "role",

Voir le fichier

@ -29,11 +29,11 @@
},
{
"id":"username",
"translation": "별명"
"translation": "아이디"
},
{
"id":"email_address_or_username",
"translation": "이메일 주소 혹은 별명"
"translation": "이메일 주소 또는 아이디"
},
{
"id":"email_address",
@ -61,7 +61,7 @@
},
{
"id":"register",
"translation": "가입"
"translation": "회원가입"
},
{
"id":"terms_conditions",
@ -73,15 +73,15 @@
},
{
"id":"remember_me",
"translation": "로그인 유지"
"translation": "로그인 상태 유지"
},
{
"id":"forgot_password",
"translation": "암호를 잊어버리셨나요?"
"translation": "비밀번호 찾기"
},
{
"id":"sign_in_box_title",
"translation": "로그인하세요"
"translation": "로그인 하세요"
},
{
"id":"sign_in_title",
@ -328,7 +328,7 @@
"translation": "토렌트 파일"
},
{
"id": "uploading_torrent_prefills_fields",
"id": "uploading_file_prefills_fields",
"translation": "토렌트 파일을 업로드하면 몇몇 필드가 자동으로 채워집니다. 추천드립니다."
},
{

Le diff du fichier est caché, car celui-ci est trop grand Voir la diff

Voir le fichier

@ -328,7 +328,7 @@
"translation": "Торрент-файл"
},
{
"id": "uploading_torrent_prefills_fields",
"id": "uploading_file_prefills_fields",
"translation": "Загрузка торрент-файла позволяет предварительно заполнить некоторые поля, это рекомендуется."
},
{

Voir le fichier

@ -5,7 +5,7 @@
},
{
"id": "verify_email_title",
"translation": "ยืนยันอีเมลสำหรับ NyaaPantsu."
"translation": "ยืนยันอีเมลสำหรับ NyaaPantsu"
},
{
"id": "verify_email_content",
@ -249,7 +249,7 @@
},
{
"id": "future_not_looking_good",
"translation": "อนาคตข้างหน้าสำหรับ Nyaa ดูไม่ดีเท่าไหร่ (ตายสนิท)"
"translation": "และอนาคตข้างหน้าสำหรับ Nyaa ดูจะไม่ดีเท่าไหร่ (ตายสนิท)"
},
{
"id": "recovery_effort",
@ -273,7 +273,7 @@
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "สำหรับ Sukebei นั่นโชคร้ายหน่อย ที่ในตอนนี้เรามีฐานข้อมูลของ Sukebei ถึงแค่ช่วงปี 2016 แต่อาจจะมีฐานข้อมูลที่ใหม่กว่าให้ใช้ก็ได้"
"translation": "สำหรับ Sukebei นั่นโชคร้ายหน่อย ที่ในตอนนี้เรามีฐานข้อมูลของ Sukebei ถึงแค่ช่วงปี 2016 แต่อาจจะมีฐานข้อมูลที่ใหม่กว่านี้ให้ใช้ก็ได้"
},
{
"id": "how_are_we_recovering",
@ -281,15 +281,15 @@
},
{
"id": "answer_how_are_we_recovering",
"translation": "ฐานข้อมูลดังกล่าวถูกใช้งานกับ nyaa.pantsu.cat และ sukebei.pantsu.cat ซึ่งมีระบบค้นหา และระบบของ Nyaa (เกือบ) ครบถ้วนควรจะมาในเร็วๆ นี้ สถิติผู้ปล่อย/ผู้โหลดอาจจะกู้กลับมาผ่านการ scraping ในอาคนอันใกล้ตั้งแต่ฟีเจอร์อื่นถูกให้ความสำคัญมากกว่าในตอนนี้"
"translation": "ฐานข้อมูลดังกล่าวถูกใช้งานกับ nyaa.pantsu.cat และ sukebei.pantsu.cat ซึ่งมีระบบค้นหา และระบบของ Nyaa (เกือบ) ครบถ้วนควรจะมาในเร็วๆ นี้ สถิติผู้ปล่อย/ผู้โหลดที่อาจจะกู้กลับมาผ่านการ Scraping ในอนาคตอันใกล้ตั้งแต่ฟีเจอร์อื่นถูกให้ความสำคัญมากกว่าในตอนนี้"
},
{
"id": "are_the_trackers_working",
"translation": "ตัวทอร์เรนท์ยังใช้ได้รึเปล่า?"
"translation": "ตัวทอร์เรนท์จะยังใช้ได้รึเปล่า?"
},
{
"id": "answer_are_the_trackers_working",
"translation": "ถ้าตัวทอร์เรนท์ใช้ไม่ได้ ผู้ปล่อย Seed จะยังเชื่อมต่อกันผ่านเครือข่าย DHT ที่ไร้ศูนย์กลาง ตราบที่ไฟล์ยังอยู่ในเครือข่าย DHT ก็ยังโหลดได้"
"translation": "ถ้าแทรคเกอร์ล่ม ผู้ปล่อย Seed จะยังเชื่อมต่อกันผ่านเครือข่าย DHT ที่ไร้ศูนย์กลาง ตราบที่ไฟล์ยังอยู่ในเครือข่าย DHT ก็ยังโหลดได้"
},
{
"id": "how_do_i_download_the_torrents",
@ -301,7 +301,7 @@
},
{
"id": "magnet_link_should_look_like",
"translation": "ตัว magnet link ควรจะเป็นลักษณะนี้:"
"translation": "ตัว Magnet link ควรจะเป็นลักษณะนี้:"
},
{
"id": "which_trackers_do_you_recommend",

Voir le fichier

@ -92,7 +92,7 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
if tag.Get("needed") != "" && formElem.Field(i).String() == "" {
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
}
if tag.Get("default") != "" {
if formElem.Field(i).String() == "" && tag.Get("default") != "" {
formElem.Field(i).SetString(tag.Get("default"))
}
case "int":
@ -105,7 +105,7 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
if tag.Get("needed") != "" && formElem.Field(i).Int() == 0 {
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
}
if tag.Get("default") != "" {
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
defaultValue, _ := strconv.Atoi(tag.Get("default"))
formElem.Field(i).SetInt(int64(defaultValue))
}
@ -119,7 +119,7 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
if tag.Get("needed") != "" && formElem.Field(i).Float() == 0 {
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
}
if tag.Get("default") != "" {
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
defaultValue, _ := strconv.Atoi(tag.Get("default"))
formElem.Field(i).SetFloat(float64(defaultValue))
}
@ -130,10 +130,6 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Wrong value for the input: %s", inputName))
}
}
if tag.Get("default") != "" {
defaultValue, _ := strconv.ParseBool(tag.Get("default"))
formElem.Field(i).SetBool(defaultValue)
}
}
}
return errorForm

Voir le fichier

@ -11,6 +11,7 @@ import (
"github.com/ewhal/nyaa/common"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service"
"github.com/ewhal/nyaa/service/torrent"
"github.com/ewhal/nyaa/util/log"
)
@ -96,7 +97,7 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool) (
}
tor, count, err = cache.Get(search, func() (tor []model.Torrent, count int, err error) {
parameters := torrentService.WhereParams{
parameters := serviceBase.WhereParams{
Params: make([]interface{}, 0, 64),
}
conditions := make([]string, 0, 64)

26
util/signals/closers.go Fichier normal
Voir le fichier

@ -0,0 +1,26 @@
package signals
import (
"io"
"sync"
)
var (
closeAccess sync.Mutex
closers []io.Closer
)
// RegisterCloser adds an io.Closer to close on interrupt
func RegisterCloser(c io.Closer) {
closeAccess.Lock()
closers = append(closers, c)
closeAccess.Unlock()
}
func closeClosers() {
closeAccess.Lock()
for idx := range closers {
closers[idx].Close()
}
closeAccess.Unlock()
}

7
util/signals/interrupt.go Fichier normal
Voir le fichier

@ -0,0 +1,7 @@
package signals
// handle interrupt signal, platform independent
func interrupted() {
closeClosers()
handleInterrupt()
}

Voir le fichier

@ -21,7 +21,7 @@ func handleReload() {
// returns when done
func Handle() {
chnl := make(chan os.Signal)
signal.Notify(chnl, syscall.SIGHUP)
signal.Notify(chnl, syscall.SIGHUP, os.Interrupt)
for {
sig, ok := <-chnl
if !ok {
@ -31,8 +31,17 @@ func Handle() {
case syscall.SIGHUP:
handleReload()
break
case os.Interrupt:
interrupted()
return
default:
break
}
}
}
// unix implementation of interrupt
// called in interrupted()
func handleInterrupt() {
// XXX: put unix specific cleanup here as needed
}

Voir le fichier

@ -2,7 +2,34 @@
package signals
import (
"os"
"os/signal"
)
func Handle() {
// windows has no sighup LOOOOL, this does nothing
// TODO: Something about SIGHUP for Windows
chnl := make(chan os.Signal)
signal.Notify(chnl, os.Interrupt)
for {
sig, ok := <-chnl
if !ok {
break
}
switch sig {
case os.Interrupt:
// this also closes listeners
interrupted()
return
default:
break
}
}
}
// win32 interrupt handler
// called in interrupted()
func handleInterrupt() {
// XXX: put any win32 specific cleanup here as needed
}