Merge branch 'master' of https://github.com/ewhal/nyaa
Cette révision appartient à :
révision
3c42b2bda0
32 fichiers modifiés avec 351 ajouts et 216 suppressions
5
.gitignore
externe
5
.gitignore
externe
|
@ -1,5 +1,6 @@
|
|||
*.sqlite
|
||||
*.db
|
||||
*.sql
|
||||
main
|
||||
nyaa
|
||||
nyaa.exe
|
||||
|
@ -8,4 +9,6 @@ nyaa-master.exe
|
|||
*.swp
|
||||
.vscode
|
||||
templates/*.html.go
|
||||
*.bat
|
||||
*.bat
|
||||
*.backup
|
||||
tags
|
||||
|
|
|
@ -6,15 +6,13 @@ install:
|
|||
- go get github.com/gorilla/mux
|
||||
- go get github.com/mattn/go-sqlite3
|
||||
- go get github.com/jinzhu/gorm
|
||||
- go get github.com/lib/pq
|
||||
- go get github.com/Sirupsen/logrus
|
||||
- go get gopkg.in/natefinch/lumberjack.v2
|
||||
- go get gopkg.in/gomail.v2
|
||||
- go get github.com/gorilla/securecookie
|
||||
- go get golang.org/x/crypto/bcrypt
|
||||
- go get github.com/nicksnyder/go-i18n/i18n
|
||||
- go get github.com/valyala/quicktemplate
|
||||
- go get github.com/valyala/quicktemplate/qtc
|
||||
- go generate ./...
|
||||
- go build
|
||||
deploy:
|
||||
provider: releases
|
||||
|
|
26
README.md
26
README.md
|
@ -21,8 +21,6 @@ that anyone will be able to deploy locally or remotely.
|
|||
|
||||
Type `./nyaa -h` for the list of options.
|
||||
|
||||
After modifying the files in `./templates`, run `go generate ./... && go build`.
|
||||
|
||||
## Systemd
|
||||
|
||||
* Edit the unit file `os/nyaa.service` to your liking
|
||||
|
@ -37,6 +35,30 @@ The provided unit file uses options directly; if you prefer a config file, do th
|
|||
* Edit `nyaa.conf` to your liking
|
||||
* Replace in the unit file the options by `-conf /etc/nyaa.conf`
|
||||
|
||||
|
||||
## Docker
|
||||
|
||||
We support docker for easy development and deployment. Simply install docker and
|
||||
docker-compose by following the instructions [here](https://docs.docker.com/engine/installation/linux/ubuntu/#install-using-the-repository).
|
||||
|
||||
Once you've successfully installed docker, make sure you have the database file
|
||||
in the project's directory as nyaa.db. Then, follow these steps to build and run
|
||||
the application.
|
||||
|
||||
```sh
|
||||
# Make sure the project is in here $GOPATH/src/github.com/ewhal/nyaa
|
||||
$ cd deploy/
|
||||
# You may choose another backend by pointing to the
|
||||
# appropriate docker-compose file.
|
||||
$ docker-compose -f docker-compose.sqlite.yml build
|
||||
$ docker-compose -f docker-compose.sqlite.yml up
|
||||
```
|
||||
|
||||
Access the website by going to [localhost:9999](http://localhost:9999).
|
||||
|
||||
> For postgres, place the dump in the toplevel directory and name it to
|
||||
> nyaa_psql.backup.
|
||||
|
||||
## TODO
|
||||
|
||||
### Features until stable release
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/ewhal/nyaa/util/log"
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
// _ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
|
|
3
deploy/.env
Fichier normal
3
deploy/.env
Fichier normal
|
@ -0,0 +1,3 @@
|
|||
PANTSU_EXTERNAL_PORT=9999
|
||||
PANTSU_INTERNAL_PORT=9999
|
||||
PANTSU_POSTGRES_DBFILE=nyaa_psql.backup
|
5
deploy/Dockerfile
Fichier normal
5
deploy/Dockerfile
Fichier normal
|
@ -0,0 +1,5 @@
|
|||
FROM golang:1.8.1
|
||||
|
||||
RUN mkdir -p /nyaa
|
||||
|
||||
WORKDIR /nyaa
|
19
deploy/docker-compose.postgres-prod.yml
Fichier normal
19
deploy/docker-compose.postgres-prod.yml
Fichier normal
|
@ -0,0 +1,19 @@
|
|||
version: '3'
|
||||
services:
|
||||
pantsu:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile
|
||||
command: ./deploy/init.sh
|
||||
env_file:
|
||||
- postgres-prod.env
|
||||
environment:
|
||||
- PANTSU_INTERNAL_PORT=${PANTSU_INTERNAL_PORT}
|
||||
ports:
|
||||
# 0.0.0.0 makes it accessible to the network
|
||||
# You may want to remove it to make pantsu available only
|
||||
# to localhost
|
||||
- 0.0.0.0:${PANTSU_EXTERNAL_PORT}:${PANTSU_INTERNAL_PORT}
|
||||
volumes:
|
||||
- "${GOPATH}:/go/"
|
||||
- "./..:/nyaa"
|
34
deploy/docker-compose.postgres.yml
Fichier normal
34
deploy/docker-compose.postgres.yml
Fichier normal
|
@ -0,0 +1,34 @@
|
|||
version: '3'
|
||||
services:
|
||||
pantsu:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile
|
||||
command: ./deploy/init.sh
|
||||
depends_on:
|
||||
- pantsu_db
|
||||
env_file:
|
||||
- postgres.env
|
||||
environment:
|
||||
- PANTSU_INTERNAL_PORT=${PANTSU_INTERNAL_PORT}
|
||||
links:
|
||||
- pantsu_db
|
||||
ports:
|
||||
# 0.0.0.0 makes it accessible to the network
|
||||
# You may want to remove it to make pantsu available only
|
||||
# to localhost
|
||||
- 0.0.0.0:${PANTSU_EXTERNAL_PORT}:${PANTSU_INTERNAL_PORT}
|
||||
volumes:
|
||||
- "${GOPATH}:/go/"
|
||||
- "./..:/nyaa"
|
||||
|
||||
pantsu_db:
|
||||
env_file:
|
||||
- postgres.env
|
||||
environment:
|
||||
- PANTSU_POSTGRES_DBFILE=${PANTSU_POSTGRES_DBFILE}
|
||||
image: postgres:9.6.2
|
||||
volumes:
|
||||
# restore.sh will be sourced after initial initdb
|
||||
- "./restore.sh:/docker-entrypoint-initdb.d/restore.sh"
|
||||
- "./../${PANTSU_POSTGRES_DBFILE}:/${PANTSU_POSTGRES_DBFILE}"
|
19
deploy/docker-compose.sqlite.yml
Fichier normal
19
deploy/docker-compose.sqlite.yml
Fichier normal
|
@ -0,0 +1,19 @@
|
|||
version: '3'
|
||||
services:
|
||||
pantsu:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile
|
||||
command: ./deploy/init.sh
|
||||
env_file:
|
||||
- sqlite.env
|
||||
environment:
|
||||
- PANTSU_INTERNAL_PORT=${PANTSU_INTERNAL_PORT}
|
||||
ports:
|
||||
# 0.0.0.0 makes it accessible to the network
|
||||
# You may want to remove it to make pantsu available only
|
||||
# to localhost
|
||||
- 0.0.0.0:${PANTSU_EXTERNAL_PORT}:${PANTSU_INTERNAL_PORT}
|
||||
volumes:
|
||||
- "${GOPATH}:/go/"
|
||||
- "./..:/nyaa"
|
16
deploy/init.sh
Fichier exécutable
16
deploy/init.sh
Fichier exécutable
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eux
|
||||
|
||||
# TODO Doesn't scale, find another way to wait until db is ready
|
||||
if [[ "${PANTSU_DBTYPE}" = "postgres" ]]; then
|
||||
echo 'Waiting for the database to be ready...'
|
||||
sleep 40
|
||||
fi
|
||||
|
||||
go get github.com/ewhal/nyaa
|
||||
go build
|
||||
./nyaa -host 0.0.0.0 \
|
||||
-port "${PANTSU_INTERNAL_PORT}" \
|
||||
-dbtype "${PANTSU_DBTYPE}" \
|
||||
-dbparams "${PANTSU_DBPARAMS}"
|
9
deploy/postgres-prod.env
Fichier normal
9
deploy/postgres-prod.env
Fichier normal
|
@ -0,0 +1,9 @@
|
|||
POSTGRES_USER=nyaapantsu
|
||||
POSTGRES_PASSWORD=nyaapantsu
|
||||
POSTGRES_DB=nyaapantsu
|
||||
|
||||
PANTSU_DBTYPE=postgres
|
||||
# TODO Would prefer to use the line below but docker doesn't seem to accept
|
||||
# that.
|
||||
#PANTSU_DBPARAMS=host=pantsu_db user=${POSTGRES_USER} dbname=${POSTGRES_DB} sslmode=disable password=${POSTGRES_PASSWORD}
|
||||
PANTSU_DBPARAMS=host=localhost user=nyaapantsu dbname=nyaapantsu sslmode=disable password=nyaapantsu
|
9
deploy/postgres.env
Fichier normal
9
deploy/postgres.env
Fichier normal
|
@ -0,0 +1,9 @@
|
|||
POSTGRES_USER=nyaapantsu
|
||||
POSTGRES_PASSWORD=nyaapantsu
|
||||
POSTGRES_DB=nyaapantsu
|
||||
|
||||
PANTSU_DBTYPE=postgres
|
||||
# TODO Would prefer to use the line below but docker doesn't seem to accept
|
||||
# that.
|
||||
#PANTSU_DBPARAMS=host=pantsu_db user=${POSTGRES_USER} dbname=${POSTGRES_DB} sslmode=disable password=${POSTGRES_PASSWORD}
|
||||
PANTSU_DBPARAMS=host=pantsu_db user=nyaapantsu dbname=nyaapantsu sslmode=disable password=nyaapantsu
|
11
deploy/prune_docker.sh
Fichier exécutable
11
deploy/prune_docker.sh
Fichier exécutable
|
@ -0,0 +1,11 @@
|
|||
# Prune images and volumes
|
||||
#
|
||||
# Docker tends to take a lot of space. This will remove dangling images and
|
||||
# volumes not used by at least container.
|
||||
# WARNING: You might not want to run this if you have stuff that are dangling
|
||||
# that you want to keep.
|
||||
#
|
||||
#!/bin/bash
|
||||
|
||||
docker image prune -f
|
||||
docker volume prune -f
|
4
deploy/restore.sh
Fichier exécutable
4
deploy/restore.sh
Fichier exécutable
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
# Restore the database from a postgres dump
|
||||
|
||||
pg_restore --username "${POSTGRES_USER}" -d ${POSTGRES_DB} "/${PANTSU_POSTGRES_DBFILE}"
|
2
deploy/sqlite.env
Fichier normal
2
deploy/sqlite.env
Fichier normal
|
@ -0,0 +1,2 @@
|
|||
PANTSU_DBTYPE=sqlite3
|
||||
PANTSU_DBPARAMS=./nyaa.db?cache_size=50
|
6
main.go
6
main.go
|
@ -25,11 +25,13 @@ func RunServer(conf *config.Config) {
|
|||
http.Handle("/", router.Router)
|
||||
|
||||
// Set up server,
|
||||
addr := fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
||||
srv := &http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", conf.Host, conf.Port),
|
||||
Addr: addr,
|
||||
WriteTimeout: 15 * time.Second,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
}
|
||||
log.Infof("listening on %s", addr)
|
||||
|
||||
err := srv.ListenAndServe()
|
||||
log.CheckError(err)
|
||||
|
@ -54,4 +56,4 @@ func main() {
|
|||
initI18N()
|
||||
RunServer(conf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ type TorrentsJson struct {
|
|||
func (t *Torrents) ToJson() TorrentsJson {
|
||||
magnet := "magnet:?xt=urn:btih:" + strings.TrimSpace(t.Hash) + "&dn=" + t.Name + config.Trackers
|
||||
b := []CommentsJson{}
|
||||
_ = json.Unmarshal([]byte(util.UnZlib(t.Comments)), &b)
|
||||
_ = json.Unmarshal([]byte(t.Comments), &b)
|
||||
res := TorrentsJson{
|
||||
Id: strconv.Itoa(t.Id),
|
||||
Name: html.UnescapeString(t.Name),
|
||||
|
@ -83,7 +83,7 @@ func (t *Torrents) ToJson() TorrentsJson {
|
|||
Hash: t.Hash,
|
||||
Date: time.Unix(t.Date, 0).Format(time.RFC3339),
|
||||
Filesize: util.FormatFilesize(t.Filesize),
|
||||
Description: template.HTML(util.UnZlib(t.Description)),
|
||||
Description: template.HTML(t.Description),
|
||||
Comments: b,
|
||||
Sub_Category: strconv.Itoa(t.Sub_Category),
|
||||
Category: strconv.Itoa(t.Category),
|
||||
|
|
Fichier binaire non affiché.
Avant Largeur: | Hauteur: | Taille: 4,6 Kio Après Largeur: | Hauteur: | Taille: 6,1 Kio |
|
@ -4,16 +4,20 @@ import (
|
|||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/ewhal/nyaa/templates"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var faqTemplate = template.Must(template.New("FAQ").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/FAQ.html"))
|
||||
|
||||
func init() {
|
||||
// common
|
||||
template.Must(faqTemplate.ParseGlob("templates/_*.html"))
|
||||
}
|
||||
|
||||
func FaqHandler(w http.ResponseWriter, r *http.Request) {
|
||||
searchForm := templates.NewSearchForm()
|
||||
searchForm := NewSearchForm()
|
||||
searchForm.HideAdvancedSearch = true
|
||||
err := faqTemplate.ExecuteTemplate(w, "index.html", FaqTemplateVariables{templates.Navigation{}, searchForm, r.URL, mux.CurrentRoute(r)})
|
||||
err := faqTemplate.ExecuteTemplate(w, "index.html", FaqTemplateVariables{Navigation{}, searchForm, r.URL, mux.CurrentRoute(r)})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service/torrent"
|
||||
"github.com/gorilla/mux"
|
||||
"html"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service/torrent"
|
||||
"github.com/ewhal/nyaa/templates"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var homeTemplate = template.Must(template.New("home").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/home.html"))
|
||||
|
||||
func init() {
|
||||
template.Must(homeTemplate.ParseGlob("templates/_*.html")) // common
|
||||
}
|
||||
|
||||
func HomeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
page := vars["page"]
|
||||
|
@ -38,8 +40,8 @@ func HomeHandler(w http.ResponseWriter, r *http.Request) {
|
|||
b = append(b, res)
|
||||
}
|
||||
|
||||
navigationTorrents := templates.Navigation{nbTorrents, maxPerPage, pagenum, "search_page"}
|
||||
htv := HomeTemplateVariables{b, templates.NewSearchForm(), navigationTorrents, r.URL, mux.CurrentRoute(r)}
|
||||
navigationTorrents := Navigation{nbTorrents, maxPerPage, pagenum, "search_page"}
|
||||
htv := HomeTemplateVariables{b, NewSearchForm(), navigationTorrents, r.URL, mux.CurrentRoute(r)}
|
||||
|
||||
err := homeTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
if err != nil {
|
||||
|
|
|
@ -5,15 +5,17 @@ import (
|
|||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/templates"
|
||||
"github.com/ewhal/nyaa/util/search"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var searchTemplate = template.Must(template.New("home").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/home.html"))
|
||||
|
||||
func init() {
|
||||
template.Must(searchTemplate.ParseGlob("templates/_*.html")) // common
|
||||
}
|
||||
|
||||
func SearchHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
page := vars["page"]
|
||||
|
@ -33,8 +35,8 @@ func SearchHandler(w http.ResponseWriter, r *http.Request) {
|
|||
b = append(b, res)
|
||||
}
|
||||
|
||||
navigationTorrents := templates.Navigation{nbTorrents, search_param.Max, pagenum, "search_page"}
|
||||
searchForm := templates.SearchForm{
|
||||
navigationTorrents := Navigation{nbTorrents, search_param.Max, pagenum, "search_page"}
|
||||
searchForm := SearchForm{
|
||||
search_param.Query,
|
||||
search_param.Status,
|
||||
search_param.Category,
|
||||
|
|
|
@ -7,9 +7,7 @@ import (
|
|||
"net/url"
|
||||
"strconv"
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
|
||||
"github.com/ewhal/nyaa/templates"
|
||||
)
|
||||
)
|
||||
|
||||
var FuncMap = template.FuncMap{
|
||||
"min": math.Min,
|
||||
|
@ -23,11 +21,11 @@ var FuncMap = template.FuncMap{
|
|||
"genRouteWithQuery": func(name string, currentUrl *url.URL, params ...string) template.HTML {
|
||||
url, err := Router.Get(name).URL(params...)
|
||||
if err == nil {
|
||||
return template.HTML(url.String() + "?" + currentUrl.RawQuery)
|
||||
return template.HTML(url.String()+ "?" + currentUrl.RawQuery)
|
||||
}
|
||||
return "error"
|
||||
},
|
||||
"genNav": func(nav templates.Navigation, currentUrl *url.URL, pagesSelectable int) template.HTML {
|
||||
"genNav": func(nav Navigation, currentUrl *url.URL, pagesSelectable int) template.HTML {
|
||||
maxPages := math.Ceil(float64(nav.TotalItem) / float64(nav.MaxItemPerPage))
|
||||
|
||||
var ret = ""
|
||||
|
@ -60,14 +58,5 @@ var FuncMap = template.FuncMap{
|
|||
}
|
||||
return template.HTML(ret)
|
||||
},
|
||||
"SearchCommon": func(s templates.SearchForm) template.HTML {
|
||||
return template.HTML(templates.SearchCommon(s))
|
||||
},
|
||||
"SearchButton": func(s templates.SearchForm) template.HTML {
|
||||
return template.HTML(templates.SearchButton(s))
|
||||
},
|
||||
"SearchAdvanced": func(nav templates.Navigation, s templates.SearchForm) template.HTML {
|
||||
return template.HTML(templates.SearchAdvanced(nav, s))
|
||||
},
|
||||
"T": i18n.IdentityTfunc,
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"net/url"
|
||||
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/templates"
|
||||
userForms "github.com/ewhal/nyaa/service/user/form"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
@ -16,40 +15,86 @@ import (
|
|||
*/
|
||||
|
||||
type FaqTemplateVariables struct {
|
||||
Navigation templates.Navigation
|
||||
Search templates.SearchForm
|
||||
Navigation Navigation
|
||||
Search SearchForm
|
||||
URL *url.URL // For parsing Url in templates
|
||||
Route *mux.Route // For getting current route in templates
|
||||
}
|
||||
|
||||
type ViewTemplateVariables struct {
|
||||
Torrent model.TorrentsJson
|
||||
Search templates.SearchForm
|
||||
Navigation templates.Navigation
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
URL *url.URL // For parsing Url in templates
|
||||
Route *mux.Route // For getting current route in templates
|
||||
}
|
||||
|
||||
type UserRegisterTemplateVariables struct {
|
||||
RegistrationForm userForms.RegistrationForm
|
||||
Search templates.SearchForm
|
||||
Navigation templates.Navigation
|
||||
URL *url.URL // For parsing Url in templates
|
||||
Route *mux.Route // For getting current route in templates
|
||||
RegistrationForm userForms.RegistrationForm
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
URL *url.URL // For parsing Url in templates
|
||||
Route *mux.Route // For getting current route in templates
|
||||
}
|
||||
|
||||
type HomeTemplateVariables struct {
|
||||
ListTorrents []model.TorrentsJson
|
||||
Search templates.SearchForm
|
||||
Navigation templates.Navigation
|
||||
URL *url.URL // For parsing Url in templates
|
||||
Route *mux.Route // For getting current route in templates
|
||||
ListTorrents []model.TorrentsJson
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
URL *url.URL // For parsing Url in templates
|
||||
Route *mux.Route // For getting current route in templates
|
||||
}
|
||||
|
||||
type UploadTemplateVariables struct {
|
||||
Upload UploadForm
|
||||
Search templates.SearchForm
|
||||
Navigation templates.Navigation
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
URL *url.URL
|
||||
Route *mux.Route
|
||||
}
|
||||
|
||||
/*
|
||||
* Variables used by the upper ones
|
||||
*/
|
||||
type Navigation struct {
|
||||
TotalItem int
|
||||
MaxItemPerPage int
|
||||
CurrentPage int
|
||||
Route string
|
||||
}
|
||||
|
||||
type SearchForm struct {
|
||||
Query string
|
||||
Status string
|
||||
Category string
|
||||
Sort string
|
||||
Order string
|
||||
HideAdvancedSearch bool
|
||||
}
|
||||
|
||||
// Some Default Values to ease things out
|
||||
func NewSearchForm(params ...string) (searchForm SearchForm) {
|
||||
if len(params) > 1 {
|
||||
searchForm.Category = params[0]
|
||||
} else {
|
||||
searchForm.Category = "_"
|
||||
}
|
||||
if len(params) > 2 {
|
||||
searchForm.Sort = params[1]
|
||||
} else {
|
||||
searchForm.Sort = "torrent_id"
|
||||
}
|
||||
if len(params) > 3 {
|
||||
order := params[2]
|
||||
if order == "DESC" {
|
||||
searchForm.Order = order
|
||||
} else if order == "ASC" {
|
||||
searchForm.Order = order
|
||||
} else {
|
||||
// TODO: handle invalid value (?)
|
||||
}
|
||||
} else {
|
||||
searchForm.Order = "DESC"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -4,12 +4,15 @@ import (
|
|||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/ewhal/nyaa/templates"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var uploadTemplate = template.Must(template.New("upload").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/upload.html"))
|
||||
|
||||
func init() {
|
||||
template.Must(uploadTemplate.ParseGlob("templates/_*.html")) // common
|
||||
}
|
||||
|
||||
func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var uploadForm UploadForm
|
||||
|
@ -21,7 +24,7 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
//add to db and redirect depending on result
|
||||
}
|
||||
} else if r.Method == "GET" {
|
||||
htv := UploadTemplateVariables{uploadForm, templates.NewSearchForm(), templates.Navigation{}, r.URL, mux.CurrentRoute(r)}
|
||||
htv := UploadTemplateVariables{uploadForm, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
|
||||
err = uploadTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package router
|
||||
|
||||
import(
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/ewhal/nyaa/templates"
|
||||
"github.com/ewhal/nyaa/service/user/form"
|
||||
"github.com/ewhal/nyaa/util/modelHelper"
|
||||
"github.com/ewhal/nyaa/util/languages"
|
||||
|
@ -15,14 +14,18 @@ var viewRegisterTemplate = template.Must(template.New("userRegister").Funcs(Func
|
|||
//var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html"))
|
||||
//var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html"))
|
||||
|
||||
func init() {
|
||||
template.Must(viewRegisterTemplate.ParseGlob("templates/_*.html"))
|
||||
}
|
||||
|
||||
// Getting View User Registration
|
||||
|
||||
func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) {
|
||||
b := form.RegistrationForm{}
|
||||
modelHelper.BindValueForm(&b, r)
|
||||
languages.SetTranslation("en-us", viewRegisterTemplate)
|
||||
htv := UserRegisterTemplateVariables{b, templates.NewSearchForm(), templates.Navigation{}, r.URL, mux.CurrentRoute(r)}
|
||||
err := viewRegisterTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
htv := UserRegisterTemplateVariables{b, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
|
||||
err := viewTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
@ -38,7 +41,7 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
}
|
||||
|
||||
// Getting View User Profile Update
|
||||
// Getting View User Profile Update
|
||||
func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
@ -57,4 +60,3 @@ func UserLoginPostHandler(w http.ResponseWriter, r *http.Request) {
|
|||
func UserProfilePostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,15 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/ewhal/nyaa/service/torrent"
|
||||
"github.com/ewhal/nyaa/templates"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html"))
|
||||
|
||||
func init() {
|
||||
template.Must(viewTemplate.ParseGlob("templates/_*.html")) // common
|
||||
}
|
||||
|
||||
func ViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
@ -18,7 +21,7 @@ func ViewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
torrent, err := torrentService.GetTorrentById(id)
|
||||
b := torrent.ToJson()
|
||||
|
||||
htv := ViewTemplateVariables{b, templates.NewSearchForm(), templates.Navigation{}, r.URL, mux.CurrentRoute(r)}
|
||||
htv := ViewTemplateVariables{b, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
|
||||
|
||||
err = viewTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
if err != nil {
|
||||
|
|
|
@ -71,7 +71,6 @@ func GetTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offs
|
|||
}
|
||||
dbQuery.Order(orderBy).Find(&torrents)
|
||||
return torrents, count
|
||||
|
||||
}
|
||||
|
||||
/* Functions to simplify the get parameters of the main function
|
||||
|
|
73
templates/_search.html
Fichier normal
73
templates/_search.html
Fichier normal
|
@ -0,0 +1,73 @@
|
|||
{{define "search_common"}}
|
||||
<select name="c" class="form-control input-sm" value>
|
||||
<option value="_">All categories</option>
|
||||
<option value="3_" {{if eq .Search.Category "3_"}}selected{{end}}>Anime</option>
|
||||
<option value="3_12" {{if eq .Search.Category "3_12"}}selected{{end}}>Anime - Anime Music Video</option>
|
||||
<option value="3_5" {{if eq .Search.Category "3_5"}}selected{{end}}>Anime - English-translated</option>
|
||||
<option value="3_13" {{if eq .Search.Category "3_13"}}selected{{end}}>Anime - Non-English-translated</option>
|
||||
<option value="3_6" {{if eq .Search.Category "3_6"}}selected{{end}}>Anime - Raw</option>
|
||||
<option value="2_" {{if eq .Search.Category "2_"}}selected{{end}}>Audio</option>
|
||||
<option value="2_3" {{if eq .Search.Category "2_3"}}selected{{end}}>Audio - Lossless</option>
|
||||
<option value="2_4" {{if eq .Search.Category "2_4"}}selected{{end}}>Audio - Lossy</option>
|
||||
<option value="4_" {{if eq .Search.Category "4_"}}selected{{end}}>Literature</option>
|
||||
<option value="4_7" {{if eq .Search.Category "4_7"}}selected{{end}}>Literature - English-translated</option>
|
||||
<option value="4_8" {{if eq .Search.Category "4_8"}}selected{{end}}>Literature - Raw</option>
|
||||
<option value="4_14" {{if eq .Search.Category "4_14"}}selected{{end}}>Literature - Non-English-translated</option>
|
||||
<option value="5_" {{if eq .Search.Category "5_"}}selected{{end}}>Live Action</option>
|
||||
<option value="5_9" {{if eq .Search.Category "5_9"}}selected{{end}}>Live Action - English-translated</option>
|
||||
<option value="5_10" {{if eq .Search.Category "5_10"}}selected{{end}}>Live Action - Idol/Promotional Video</option>
|
||||
<option value="5_18" {{if eq .Search.Category "5_18"}}selected{{end}}>Live Action - Non-English-translated</option>
|
||||
<option value="5_11" {{if eq .Search.Category "5_11"}}selected{{end}}>Live Action - Raw</option>
|
||||
<option value="6_" {{if eq .Search.Category "6_"}}selected{{end}}>Pictures</option>
|
||||
<option value="6_15" {{if eq .Search.Category "6_15"}}selected{{end}}>Pictures - Graphics</option>
|
||||
<option value="6_16" {{if eq .Search.Category "6_16"}}selected{{end}}>Pictures - Photos</option>
|
||||
<option value="1_" {{if eq .Search.Category "1_"}}selected{{end}}>Software</option>
|
||||
<option value="1_1" {{if eq .Search.Category "1_1"}}selected{{end}}>Software - Applications</option>
|
||||
<option value="1_2" {{if eq .Search.Category "1_2"}}selected{{end}}>Software - Games</option>
|
||||
</select>
|
||||
<select name="s" class="form-control input-sm">
|
||||
<option value="">Show all</option>
|
||||
<option value="2" {{if eq .Search.Status "2"}}selected{{end}}>Filter Remakes</option>
|
||||
<option value="3" {{if eq .Search.Status "3"}}selected{{end}}>Trusted</option>
|
||||
<option value="4" {{if eq .Search.Status "4"}}selected{{end}}>A+</option>
|
||||
</select>
|
||||
{{end}}
|
||||
{{define "search_advanced"}}
|
||||
<select name="sort" class="form-control input-sm">
|
||||
<option value="torrent_id" {{if eq .Search.Sort "torrent_id"}}selected{{end}}>ID</option>
|
||||
<option value="torrent_name" {{if eq .Search.Sort "torrent_name"}}selected{{end}}>Name</option>
|
||||
<option value="date" {{if eq .Search.Sort "date"}}selected{{end}}>Date</option>
|
||||
<option value="downloads" {{if eq .Search.Sort "downloads"}}selected{{end}}>Downloads</option>
|
||||
<option value="filesize" {{if eq .Search.Sort "filesize"}}selected{{end}}>Size</option>
|
||||
</select>
|
||||
<select name="order" class="form-control input-sm">
|
||||
<option value="desc" {{if eq .Search.Order "desc"}}selected{{end}}>Descending</option>
|
||||
<option value="asc" {{if eq .Search.Order "asc"}}selected{{end}}>Ascending</option>
|
||||
</select>
|
||||
<select name="max" class="form-control input-sm">
|
||||
<option value="5" {{if eq .Navigation.MaxItemPerPage 5}}selected{{end}}>5</option>
|
||||
<option value="10" {{if eq .Navigation.MaxItemPerPage 10}}selected{{end}}>10</option>
|
||||
<option value="15" {{if eq .Navigation.MaxItemPerPage 15}}selected{{end}}>15</option>
|
||||
<option value="20" {{if eq .Navigation.MaxItemPerPage 20}}selected{{end}}>20</option>
|
||||
<option value="25" {{if eq .Navigation.MaxItemPerPage 25}}selected{{end}}>25</option>
|
||||
<option value="30" {{if eq .Navigation.MaxItemPerPage 30}}selected{{end}}>30</option>
|
||||
<option value="35" {{if eq .Navigation.MaxItemPerPage 35}}selected{{end}}>35</option>
|
||||
<option value="40" {{if eq .Navigation.MaxItemPerPage 40}}selected{{end}}>40</option>
|
||||
<option value="45" {{if eq .Navigation.MaxItemPerPage 45}}selected{{end}}>45</option>
|
||||
<option value="50" {{if eq .Navigation.MaxItemPerPage 50}}selected{{end}}>50</option>
|
||||
<option value="70" {{if eq .Navigation.MaxItemPerPage 70}}selected{{end}}>70</option>
|
||||
<option value="100" {{if eq .Navigation.MaxItemPerPage 100}}selected{{end}}>100</option>
|
||||
<option value="150" {{if eq .Navigation.MaxItemPerPage 150}}selected{{end}}>150</option>
|
||||
<option value="200" {{if eq .Navigation.MaxItemPerPage 200}}selected{{end}}>200</option>
|
||||
<option value="300" {{if eq .Navigation.MaxItemPerPage 300}}selected{{end}}>300</option>
|
||||
</select>
|
||||
{{end}}
|
||||
|
||||
{{define "search_button"}}
|
||||
<div class="input-group">
|
||||
<input name="q" class="form-control input-sm" placeholder="Search" type="text" value="{{.Search.Query}}">
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-sm btn-success"><span class="glyphicon glyphicon-search" aria-hidden="true"></span> Search</button>
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
|
@ -6,7 +6,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||
<title>Nyaa Pantsu - {{block "title" .}}Error 404{{end}}</title>
|
||||
<link rel="icon" type="image/png" href="/img/favicon.png?v=2" />
|
||||
<link rel="icon" type="image/png" href="/img/favicon.png?v=3" />
|
||||
|
||||
<!-- RSS Feed with Context -->
|
||||
<link href="{{ genRouteWithQuery "feed" .URL }}" rel="alternate" type="application/rss+xml" title="Nyaa Pantsu - {{block "rsstitle" .}}Last torrents{{end}} RSS Feed" />
|
||||
|
@ -50,8 +50,8 @@
|
|||
</ul>
|
||||
<form class="navbar-form navbar-right" role="search" action="/search" method="get">
|
||||
<div class="form-group">
|
||||
{{ SearchCommon .Search }}
|
||||
{{ SearchAdvanced .Navigation .Search }}
|
||||
{{block "search_common" .}}{{end}}
|
||||
{{block "search_button" .}}{{end}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -67,9 +67,9 @@
|
|||
<font size="4.5">Advanced Search</font><br />
|
||||
<form class="navbar-form" role="search" action="/search" method="get">
|
||||
<div class="form-group">
|
||||
{{ SearchCommon .Search }}
|
||||
{{ SearchAdvanced .Navigation .Search }}
|
||||
{{ SearchButton .Search }}
|
||||
{{block "search_common" .}}{{end}}
|
||||
{{block "search_advanced" .}}{{end}}
|
||||
{{block "search_button" .}}{{end}}
|
||||
</div>
|
||||
</form>
|
||||
<div style="clear:both"></div>
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
{% import "strconv" %}
|
||||
|
||||
{% code type searchField struct{
|
||||
id, name string
|
||||
} %}
|
||||
|
||||
Common seatch part of many pages
|
||||
{% func SearchCommon(search SearchForm) %}
|
||||
<select name="c" class="form-control input-sm" value>
|
||||
<option value="_">All categories</option>
|
||||
{%= searchOptions(search.Category, []searchField{
|
||||
{"3_", "Anime"},
|
||||
{"3_12", "Anime - Anime Music Video"},
|
||||
{"3_5", "Anime - English-translated"},
|
||||
{"3_13", "Anime - Non-English-translated"},
|
||||
{"3_6", "Anime - Raw"},
|
||||
{"2_", "Audio"},
|
||||
{"2_3", "Audio - Lossless"},
|
||||
{"2_4", "Audio - Lossy"},
|
||||
{"4_", "Literature"},
|
||||
{"4_7", "Literature - English-translated"},
|
||||
{"4_8", "Literature - Raw"},
|
||||
{"4_14", "Literature - Non-English-translated"},
|
||||
{"5_", "Live Action"},
|
||||
{"5_9", "Live Action - English-translated"},
|
||||
{"5_10", "Live Action - Idol/Promotional Video"},
|
||||
{"5_18", "Live Action - Non-English-translated"},
|
||||
{"5_11", "Live Action - Raw"},
|
||||
{"6_", "Pictures"},
|
||||
{"6_15", "Pictures - Graphics"},
|
||||
{"6_16", "Pictures - Photos"},
|
||||
{"1_", "Software"},
|
||||
{"1_1", "Software - Applications"},
|
||||
{"1_2", "Software - Games"},
|
||||
}) %}
|
||||
</select>
|
||||
<select name="s" class="form-control input-sm">
|
||||
<option value="">Show all</option>
|
||||
{%= searchOptions(search.Status, []searchField{
|
||||
{"2", "Filter Remakes"},
|
||||
{"3", "Trusted"},
|
||||
{"4", "A+"},
|
||||
}) %}
|
||||
</select>
|
||||
{% endfunc %}
|
||||
|
||||
Options fields of a search <select> element
|
||||
{% func searchOptions(selected string, s []searchField) %}
|
||||
{% for _, s := range s %}
|
||||
{%= searchOption(selected, s) %}
|
||||
{% endfor %}
|
||||
{% endfunc %}
|
||||
|
||||
Single search optiion field of a <select>
|
||||
{% func searchOption(selected string, s searchField) %}
|
||||
<option value="{%s= s.id %}" {% if selected == s.id %}selected{% endif %}>{%s= s.name %}</option>
|
||||
{% endfunc %}
|
||||
|
||||
{% func SearchAdvanced(nav Navigation, search SearchForm) %}
|
||||
<select name="sort" class="form-control input-sm">
|
||||
{%= searchOptions(search.Sort, []searchField{
|
||||
{"torrent_id", "ID"},
|
||||
{"torrent_name", "Name"},
|
||||
{"date", "Date"},
|
||||
{"downloads", "Downloads"},
|
||||
{"filesize", "Size"},
|
||||
}) %}
|
||||
</select>
|
||||
<select name="order" class="form-control input-sm">
|
||||
{%= searchOptions(search.Order, []searchField{
|
||||
{"desc", "Descending"},
|
||||
{"asc", "Ascending"},
|
||||
}) %}
|
||||
</select>
|
||||
<select name="max" class="form-control input-sm">
|
||||
{% code sel := strconv.Itoa(nav.MaxItemPerPage) %}
|
||||
{% for _, m := range [...]int{5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 70, 100, 150, 200, 300} %}
|
||||
{% code id := strconv.Itoa(m) %}
|
||||
{%= searchOption(sel, searchField{id, id}) %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endfunc %}
|
||||
|
||||
{% func SearchButton(search SearchForm) %}
|
||||
<div class="input-group">
|
||||
<input name="q" class="form-control input-sm" placeholder="Search" type="text" value="{%s search.Query %}">
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-sm btn-success">
|
||||
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
|
||||
Search
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
{% endfunc %}
|
|
@ -1,3 +0,0 @@
|
|||
//go:generate qtc --file search.html
|
||||
|
||||
package templates
|
|
@ -1,47 +0,0 @@
|
|||
package templates
|
||||
|
||||
/*
|
||||
* Variables used by the upper ones
|
||||
*/
|
||||
type Navigation struct {
|
||||
TotalItem int
|
||||
MaxItemPerPage int
|
||||
CurrentPage int
|
||||
Route string
|
||||
}
|
||||
|
||||
type SearchForm struct {
|
||||
Query string
|
||||
Status string
|
||||
Category string
|
||||
Sort string
|
||||
Order string
|
||||
HideAdvancedSearch bool
|
||||
}
|
||||
|
||||
// Some Default Values to ease things out
|
||||
func NewSearchForm(params ...string) (searchForm SearchForm) {
|
||||
if len(params) > 1 {
|
||||
searchForm.Category = params[0]
|
||||
} else {
|
||||
searchForm.Category = "_"
|
||||
}
|
||||
if len(params) > 2 {
|
||||
searchForm.Sort = params[1]
|
||||
} else {
|
||||
searchForm.Sort = "torrent_id"
|
||||
}
|
||||
if len(params) > 3 {
|
||||
order := params[2]
|
||||
if order == "DESC" {
|
||||
searchForm.Order = order
|
||||
} else if order == "ASC" {
|
||||
searchForm.Order = order
|
||||
} else {
|
||||
// TODO: handle invalid value (?)
|
||||
}
|
||||
} else {
|
||||
searchForm.Order = "DESC"
|
||||
}
|
||||
return
|
||||
}
|
Référencer dans un nouveau ticket