Upgraded to latest aero version

This commit is contained in:
Eduard Urbach 2019-06-01 13:55:49 +09:00
parent ae591e5e7e
commit 28db818c37
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
196 changed files with 645 additions and 593 deletions

View File

@ -19,6 +19,7 @@ var (
CSS string
ServiceWorker string
Organization string
Domain = "notify.moe"
)
// load loads all the necessary assets into memory.
@ -57,55 +58,55 @@ func load() {
CSS = css.Bundle()
}
// Configure adds all the routes used for media assets.
// Configure adds all the routes used for media
func Configure(app *aero.Application) {
load()
app.Get("/scripts", func(ctx *aero.Context) string {
app.Get("/scripts", func(ctx aero.Context) error {
return ctx.JavaScript(JS)
})
app.Get("/styles", func(ctx *aero.Context) string {
app.Get("/styles", func(ctx aero.Context) error {
return ctx.CSS(CSS)
})
app.Get("/service-worker", func(ctx *aero.Context) string {
app.Get("/service-worker", func(ctx aero.Context) error {
return ctx.JavaScript(ServiceWorker)
})
// Web manifest
app.Get("/manifest.json", func(ctx *aero.Context) string {
app.Get("/manifest.json", func(ctx aero.Context) error {
return ctx.JSON(Manifest)
})
// Favicon
app.Get("/favicon.ico", func(ctx *aero.Context) string {
ctx.Response().Header().Set("Access-Control-Allow-Origin", "*")
app.Get("/favicon.ico", func(ctx aero.Context) error {
ctx.Response().SetHeader("Access-Control-Allow-Origin", "*")
return ctx.File("images/brand/64.png")
})
// Images
app.Get("/images/*file", func(ctx *aero.Context) string {
ctx.Response().Header().Set("Access-Control-Allow-Origin", "*")
app.Get("/images/*file", func(ctx aero.Context) error {
ctx.Response().SetHeader("Access-Control-Allow-Origin", "*")
return ctx.File("images/" + ctx.Get("file"))
})
// Videos
app.Get("/videos/*file", func(ctx *aero.Context) string {
ctx.Response().Header().Set("Access-Control-Allow-Origin", "*")
app.Get("/videos/*file", func(ctx aero.Context) error {
ctx.Response().SetHeader("Access-Control-Allow-Origin", "*")
return ctx.File("videos/" + ctx.Get("file"))
})
// Audio
app.Get("/audio/*file", func(ctx *aero.Context) string {
ctx.Response().Header().Set("Access-Control-Allow-Origin", "*")
app.Get("/audio/*file", func(ctx aero.Context) error {
ctx.Response().SetHeader("Access-Control-Allow-Origin", "*")
return ctx.File("audio/" + ctx.Get("file"))
})
// Anime sitemap
app.Get("/sitemap/anime.txt", func(ctx *aero.Context) string {
app.Get("/sitemap/anime.txt", func(ctx aero.Context) error {
sitemap := sitemap.New()
prefix := "https://" + app.Config.Domain
prefix := "https://" + Domain
for anime := range arn.StreamAnime() {
sitemap.Add(prefix + anime.Link())
@ -115,9 +116,9 @@ func Configure(app *aero.Application) {
})
// Character sitemap
app.Get("/sitemap/character.txt", func(ctx *aero.Context) string {
app.Get("/sitemap/character.txt", func(ctx aero.Context) error {
sitemap := sitemap.New()
prefix := "https://" + app.Config.Domain
prefix := "https://" + Domain
for character := range arn.StreamCharacters() {
sitemap.Add(prefix + character.Link())
@ -127,9 +128,9 @@ func Configure(app *aero.Application) {
})
// User sitemap
app.Get("/sitemap/user.txt", func(ctx *aero.Context) string {
app.Get("/sitemap/user.txt", func(ctx aero.Context) error {
sitemap := sitemap.New()
prefix := "https://" + app.Config.Domain
prefix := "https://" + Domain
for user := range arn.StreamUsers() {
if !user.HasNick() {
@ -143,9 +144,9 @@ func Configure(app *aero.Application) {
})
// SoundTrack sitemap
app.Get("/sitemap/soundtrack.txt", func(ctx *aero.Context) string {
app.Get("/sitemap/soundtrack.txt", func(ctx aero.Context) error {
sitemap := sitemap.New()
prefix := "https://" + app.Config.Domain
prefix := "https://" + Domain
for soundTrack := range arn.StreamSoundTracks() {
sitemap.Add(prefix + soundTrack.Link())
@ -155,9 +156,9 @@ func Configure(app *aero.Application) {
})
// Thread sitemap
app.Get("/sitemap/thread.txt", func(ctx *aero.Context) string {
app.Get("/sitemap/thread.txt", func(ctx aero.Context) error {
sitemap := sitemap.New()
prefix := "https://" + app.Config.Domain
prefix := "https://" + Domain
for thread := range arn.StreamThreads() {
sitemap.Add(prefix + thread.Link())
@ -167,9 +168,9 @@ func Configure(app *aero.Application) {
})
// Post sitemap
app.Get("/sitemap/post.txt", func(ctx *aero.Context) string {
app.Get("/sitemap/post.txt", func(ctx aero.Context) error {
sitemap := sitemap.New()
prefix := "https://" + app.Config.Domain
prefix := "https://" + Domain
for post := range arn.StreamPosts() {
sitemap.Add(prefix + post.Link())
@ -179,7 +180,7 @@ func Configure(app *aero.Application) {
})
// For benchmarks
app.Get("/hello", func(ctx *aero.Context) string {
app.Get("/hello", func(ctx aero.Context) error {
return ctx.Text("Hello World")
})
}

View File

@ -1,6 +1,8 @@
package auth
import (
"net/http"
"github.com/aerogo/aero"
"github.com/animenotifier/notify.moe/utils"
)
@ -19,17 +21,17 @@ func Install(app *aero.Application) {
InstallTwitterAuth(app)
// Logout
app.Get("/logout", func(ctx *aero.Context) string {
app.Get("/logout", func(ctx aero.Context) error {
if ctx.HasSession() {
user := utils.GetUser(ctx)
if user != nil {
authLog.Info("%s logged out | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("%s logged out | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
}
ctx.Session().Delete("userId")
}
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
})
}

View File

@ -9,6 +9,7 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/utils"
jsoniter "github.com/json-iterator/go"
"golang.org/x/oauth2"
@ -33,7 +34,7 @@ func InstallFacebookAuth(app *aero.Application) {
config := &oauth2.Config{
ClientID: arn.APIKeys.Facebook.ID,
ClientSecret: arn.APIKeys.Facebook.Secret,
RedirectURL: "https://" + app.Config.Domain + "/auth/facebook/callback",
RedirectURL: "https://" + assets.Domain + "/auth/facebook/callback",
Scopes: []string{
"public_profile",
"email",
@ -44,10 +45,10 @@ func InstallFacebookAuth(app *aero.Application) {
// When a user visits /auth/facebook, we ask OAuth2 config for a URL
// to redirect the user to. Once the user has logged in on that page,
// he'll be redirected back to our servers to the callback page.
app.Get("/auth/facebook", func(ctx *aero.Context) string {
app.Get("/auth/facebook", func(ctx aero.Context) error {
state := ctx.Session().ID()
url := config.AuthCodeURL(state)
return ctx.Redirect(url)
return ctx.Redirect(http.StatusFound, url)
})
// This is the redirect URL that we specified in the OAuth2 config.
@ -55,7 +56,7 @@ func InstallFacebookAuth(app *aero.Application) {
// Now we have to check for fraud requests and request user information.
// If both Facebook ID and email can't be found in our DB, register a new user.
// Otherwise, log in the user with the given Facebook ID or email.
app.Get("/auth/facebook/callback", func(ctx *aero.Context) string {
app.Get("/auth/facebook/callback", func(ctx aero.Context) error {
if !ctx.HasSession() {
return ctx.Error(http.StatusUnauthorized, "Facebook login failed", errors.New("Session does not exist"))
}
@ -108,9 +109,9 @@ func InstallFacebookAuth(app *aero.Application) {
user.Save()
// Log
authLog.Info("Added Facebook ID to existing account | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("Added Facebook ID to existing account | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
var getErr error
@ -119,7 +120,7 @@ func InstallFacebookAuth(app *aero.Application) {
user, getErr = arn.GetUserByFacebookID(fbUser.ID)
if getErr == nil && user != nil {
authLog.Info("User logged in via Facebook ID | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("User logged in via Facebook ID | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
// Add FacebookToUser reference
user.ConnectFacebook(fbUser.ID)
@ -128,20 +129,20 @@ func InstallFacebookAuth(app *aero.Application) {
user.Save()
session.Set("userId", user.ID)
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
// Try to find an existing user via the associated e-mail address
user, getErr = arn.GetUserByEmail(fbUser.Email)
if getErr == nil && user != nil {
authLog.Info("User logged in via Email | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("User logged in via Email | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
user.LastLogin = arn.DateTimeUTC()
user.Save()
session.Set("userId", user.ID)
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
// Register new user
@ -169,9 +170,9 @@ func InstallFacebookAuth(app *aero.Application) {
session.Set("userId", user.ID)
// Log
authLog.Info("Registered new user via Facebook | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("Registered new user via Facebook | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
// Redirect to starting page for new users
return ctx.Redirect(newUserStartRoute)
return ctx.Redirect(http.StatusFound, newUserStartRoute)
})
}

View File

@ -9,6 +9,7 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/utils"
jsoniter "github.com/json-iterator/go"
"golang.org/x/oauth2"
@ -38,7 +39,7 @@ func InstallGoogleAuth(app *aero.Application) {
config := &oauth2.Config{
ClientID: arn.APIKeys.Google.ID,
ClientSecret: arn.APIKeys.Google.Secret,
RedirectURL: "https://" + app.Config.Domain + "/auth/google/callback",
RedirectURL: "https://" + assets.Domain + "/auth/google/callback",
Scopes: []string{
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
@ -51,10 +52,10 @@ func InstallGoogleAuth(app *aero.Application) {
// When a user visits /auth/google, we ask OAuth2 config for a URL
// to redirect the user to. Once the user has logged in on that page,
// he'll be redirected back to our servers to the callback page.
app.Get("/auth/google", func(ctx *aero.Context) string {
app.Get("/auth/google", func(ctx aero.Context) error {
state := ctx.Session().ID()
url := config.AuthCodeURL(state)
return ctx.Redirect(url)
return ctx.Redirect(http.StatusFound, url)
})
// This is the redirect URL that we specified in the OAuth2 config.
@ -62,7 +63,7 @@ func InstallGoogleAuth(app *aero.Application) {
// Now we have to check for fraud requests and request user information.
// If both Google ID and email can't be found in our DB, register a new user.
// Otherwise, log in the user with the given Google ID or email.
app.Get("/auth/google/callback", func(ctx *aero.Context) string {
app.Get("/auth/google/callback", func(ctx aero.Context) error {
if !ctx.HasSession() {
return ctx.Error(http.StatusUnauthorized, "Google login failed", errors.New("Session does not exist"))
}
@ -119,9 +120,9 @@ func InstallGoogleAuth(app *aero.Application) {
user.Save()
// Log
authLog.Info("Added Google ID to existing account | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("Added Google ID to existing account | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
var getErr error
@ -130,20 +131,20 @@ func InstallGoogleAuth(app *aero.Application) {
user, getErr = arn.GetUserByGoogleID(googleUser.Sub)
if getErr == nil && user != nil {
authLog.Info("User logged in via Google ID | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("User logged in via Google ID | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
user.LastLogin = arn.DateTimeUTC()
user.Save()
session.Set("userId", user.ID)
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
// Try to find an existing user via the associated e-mail address
user, getErr = arn.GetUserByEmail(googleUser.Email)
if getErr == nil && user != nil {
authLog.Info("User logged in via Email | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("User logged in via Email | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
// Add GoogleToUser reference
user.ConnectGoogle(googleUser.Sub)
@ -152,7 +153,7 @@ func InstallGoogleAuth(app *aero.Application) {
user.Save()
session.Set("userId", user.ID)
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
// Register new user
@ -180,9 +181,9 @@ func InstallGoogleAuth(app *aero.Application) {
session.Set("userId", user.ID)
// Log
authLog.Info("Registered new user via Google | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("Registered new user via Google | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
// Redirect to starting page for new users
return ctx.Redirect(newUserStartRoute)
return ctx.Redirect(http.StatusFound, newUserStartRoute)
})
}

View File

@ -10,6 +10,7 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/utils"
"github.com/gomodule/oauth1/oauth"
jsoniter "github.com/json-iterator/go"
@ -42,8 +43,8 @@ func InstallTwitterAuth(app *aero.Application) {
// a request token and give us a URL to redirect the user to.
// Once the user has approved the application on that page,
// he'll be redirected back to our servers to the callback page.
app.Get("/auth/twitter", func(ctx *aero.Context) string {
callback := "https://" + ctx.App.Config.Domain + "/auth/twitter/callback"
app.Get("/auth/twitter", func(ctx aero.Context) error {
callback := "https://" + assets.Domain + "/auth/twitter/callback"
tempCred, err := config.RequestTemporaryCredentials(nil, callback, nil)
if err != nil {
@ -52,7 +53,7 @@ func InstallTwitterAuth(app *aero.Application) {
ctx.Session().Set("tempCred", tempCred)
url := config.AuthorizationURL(tempCred, nil)
return ctx.Redirect(url)
return ctx.Redirect(http.StatusFound, url)
})
// This is the redirect URL that we specified in /auth/twitter.
@ -60,7 +61,7 @@ func InstallTwitterAuth(app *aero.Application) {
// Now we have to check for fraud requests and request user information.
// If both Twitter ID and email can't be found in our DB, register a new user.
// Otherwise, log in the user with the given Twitter ID or email.
app.Get("/auth/twitter/callback", func(ctx *aero.Context) string {
app.Get("/auth/twitter/callback", func(ctx aero.Context) error {
if !ctx.HasSession() {
return ctx.Error(http.StatusUnauthorized, "Twitter login failed", errors.New("Session does not exist"))
}
@ -121,9 +122,9 @@ func InstallTwitterAuth(app *aero.Application) {
user.Save()
// Log
authLog.Info("Added Twitter ID to existing account | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("Added Twitter ID to existing account | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
var getErr error
@ -132,20 +133,20 @@ func InstallTwitterAuth(app *aero.Application) {
user, getErr = arn.GetUserByTwitterID(twUser.ID)
if getErr == nil && user != nil {
authLog.Info("User logged in via Twitter ID | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("User logged in via Twitter ID | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
user.LastLogin = arn.DateTimeUTC()
user.Save()
session.Set("userId", user.ID)
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
// Try to find an existing user via the associated e-mail address
user, getErr = arn.GetUserByEmail(twUser.Email)
if getErr == nil && user != nil {
authLog.Info("User logged in via Email | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("User logged in via Email | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
// Add TwitterToUser reference
user.ConnectTwitter(twUser.ID)
@ -155,7 +156,7 @@ func InstallTwitterAuth(app *aero.Application) {
user.Save()
session.Set("userId", user.ID)
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
// Register new user
@ -187,9 +188,9 @@ func InstallTwitterAuth(app *aero.Application) {
session.Set("userId", user.ID)
// Log
authLog.Info("Registered new user via Twitter | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.RealIP(), user.Email, user.RealName())
authLog.Info("Registered new user via Twitter | %s | %s | %s | %s | %s", user.Nick, user.ID, ctx.IP(), user.Email, user.RealName())
// Redirect to starting page for new users
return ctx.Redirect(newUserStartRoute)
return ctx.Redirect(http.StatusFound, newUserStartRoute)
})
}

View File

@ -1,6 +1,4 @@
{
"domain": "notify.moe",
"title": "Anime Notifier",
"fonts": [
"Ubuntu"
],

View File

@ -16,7 +16,7 @@ import (
)
// Get returns the contents of our amazing page.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
return ctx.HTML("Hey it's me, foobar!")
}
```
@ -73,7 +73,7 @@ import (
)
// Get returns the contents of our amazing page.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
return ctx.HTML(components.FooBar())
}
```

23
go.mod
View File

@ -4,27 +4,25 @@ go 1.12
require (
cloud.google.com/go v0.39.0 // indirect
github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 // indirect
github.com/aerogo/aero v1.2.5
github.com/aerogo/api v0.1.7
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/aerogo/aero v1.3.2
github.com/aerogo/api v0.2.0
github.com/aerogo/crawler v0.2.5
github.com/aerogo/graphql v0.3.6
github.com/aerogo/graphql v0.4.0
github.com/aerogo/http v1.0.6
github.com/aerogo/layout v0.1.9
github.com/aerogo/layout v0.3.0
github.com/aerogo/log v0.2.5
github.com/aerogo/manifest v0.1.4
github.com/aerogo/markdown v0.1.8
github.com/aerogo/nano v0.3.2
github.com/aerogo/pack v0.5.0
github.com/aerogo/run v1.0.1
github.com/aerogo/session-store-nano v0.1.5
github.com/aerogo/sitemap v0.1.2
github.com/aerogo/sitemap v0.1.3
github.com/akyoto/cache v1.0.2
github.com/akyoto/color v1.8.5
github.com/akyoto/hash v0.3.3
github.com/akyoto/hash v0.3.5
github.com/akyoto/stringutils v0.2.1
github.com/animenotifier/anilist v0.2.3
github.com/animenotifier/arn v1.1.25
github.com/animenotifier/arn v1.2.0
github.com/animenotifier/kitsu v0.2.3
github.com/animenotifier/mal v0.2.3
github.com/animenotifier/shoboi v0.2.3
@ -44,11 +42,10 @@ require (
github.com/pariz/gountries v0.0.0-20171019111738-adb00f6513a3
github.com/shirou/gopsutil v2.18.12+incompatible
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect
github.com/smartystreets/assertions v1.0.0 // indirect
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
github.com/stretchr/testify v1.3.0
golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0
golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
golang.org/x/text v0.3.2 // indirect
google.golang.org/appengine v1.6.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
@ -56,4 +53,6 @@ require (
gopkg.in/yaml.v2 v2.2.2 // indirect
)
replace github.com/aerogo/layout => /home/eduard/projects/aerogo/layout
exclude github.com/logpacker/PayPal-Go-SDK v2.0.0+incompatible

115
go.sum
View File

@ -5,54 +5,34 @@ cloud.google.com/go v0.39.0 h1:UgQP9na6OTfp4dsAiz/eFpFA1C6tPdH5wiRdi19tuMw=
cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 h1:UUppSQnhf4Yc6xGxSkoQpPhb7RVzuv5Nb1mwJ5VId9s=
github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/aerogo/aero v1.1.8 h1:bV87Q15IGJY4XZdZM6jBVBp/rnGjQGIMqUh+WTC4PAY=
github.com/aerogo/aero v1.1.8/go.mod h1:Xpl5AFecG/cpx9As733/zM36UiPrpd4s12stfyx6i5w=
github.com/aerogo/aero v1.1.9 h1:EB+oTljSIfQENZTVyvnZ24Pb2UsV+Ows04/HfzOJ/3c=
github.com/aerogo/aero v1.1.9/go.mod h1:MvHPJcXZmZUu5mh4q6j5n3DijKmAMY6puhzCH1w22Vw=
github.com/aerogo/aero v1.1.13 h1:e+FXHaSRZ2a/xrLwF1Yeujh5yHBovdLfHxJYtZvPpWw=
github.com/aerogo/aero v1.1.13/go.mod h1:jcHCf+a3vExpo1SKRPrmkspO9VWiDZHC5/ITss/Sz6Y=
github.com/aerogo/aero v1.2.5 h1:xn1POlFP8gWUDv99QqetijOsghgHt5WXAEXiqhmRQbQ=
github.com/aerogo/aero v1.2.5/go.mod h1:jRXI34hJL3iwH+llwLLnMPPHr9tFgLfl0JQhaH+8eJQ=
github.com/aerogo/api v0.1.7 h1:2cEOUlPvlRnLo6A0xn8+UpmluWqRoYEGn0Ik4kxmUEI=
github.com/aerogo/api v0.1.7/go.mod h1:6uPqLd2/VzFiuC7L7hPMtUNjfRjczJQUP6Uks7EiXpw=
github.com/aerogo/cluster v0.1.5 h1:mOYQmaYRsvIi1inaGLICmeJgCYycBxiHnjcTFLtC6kc=
github.com/aerogo/cluster v0.1.5/go.mod h1:uFZAv2XWV+/clNy0iciCIP2Ygndv8rC8QCX/RCO2R2g=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/aerogo/aero v1.3.1 h1:P4XS4ePrJWSJwHLXKkX5YjFsoywhWuXf3dPIX5XVAW0=
github.com/aerogo/aero v1.3.1/go.mod h1:5rPhXo2DNMFQ7XhDsuZ3L7Zr6TH/349+WczUbrOUZvM=
github.com/aerogo/aero v1.3.2 h1:Sj/2U860HLJG5iq5het6QFgo/ERQ7cS4Zdz38xPWSwQ=
github.com/aerogo/aero v1.3.2/go.mod h1:5rPhXo2DNMFQ7XhDsuZ3L7Zr6TH/349+WczUbrOUZvM=
github.com/aerogo/api v0.2.0 h1:mIc/y381e+Qc85eSc2cKPdpDDOmT0hlnEeCw2Dcf7no=
github.com/aerogo/api v0.2.0/go.mod h1:6objJn5XiKpYpywQUPrFjxZIXD4NVI2LwcBNYCEcS3Y=
github.com/aerogo/cluster v0.1.6 h1:9HYjJwo19uuh9thIc80T3caap9t9b4BXZ1iN8aztjlU=
github.com/aerogo/cluster v0.1.6/go.mod h1:XzYmY5MqNxASmYQWFMh5iCl4tcm1ekReaCQQXVCpJtY=
github.com/aerogo/cluster v0.1.7 h1:hMG7oPzYmo4TPtzn4zJc291YzNRrnoSPWSKVaDS4ODA=
github.com/aerogo/cluster v0.1.7/go.mod h1:XzYmY5MqNxASmYQWFMh5iCl4tcm1ekReaCQQXVCpJtY=
github.com/aerogo/codetree v1.2.4 h1:Gf6JHjzUBEGn0Z+h4EK1pikexqFZc8+b/3LiLkRXhms=
github.com/aerogo/codetree v1.2.4/go.mod h1:MhElfskRtfAKGy5ukRIFcLTtHcSCwLRBjDAlsbuFts0=
github.com/aerogo/crawler v0.2.5 h1:tsaRViwBKNJAotzjEmGOln7quzka4f0goiEZVg4Fdxc=
github.com/aerogo/crawler v0.2.5/go.mod h1:nyFPwxOFm3gy5cvif8rG1ogXO0fTDVrxsQHwicJz1F8=
github.com/aerogo/csp v0.1.5 h1:4l8AaMWndSb0t/03onf4lc0th2zoaapmtKgysDIznW4=
github.com/aerogo/csp v0.1.5/go.mod h1:KNqnTFffuDwIPJxBEFTl3baBx+x3Vw+9kcMfu5APFJA=
github.com/aerogo/csp v0.1.6 h1:C2OpzPZHY1S5H+Dz0Or+YgVSfXT36avJw9e1smrIWUY=
github.com/aerogo/csp v0.1.6/go.mod h1:KNqnTFffuDwIPJxBEFTl3baBx+x3Vw+9kcMfu5APFJA=
github.com/aerogo/flow v0.1.2 h1:fZ3V7Bo7jwBjqnM1mWxYbg5O/FlQX2XovzMZU2D+o5M=
github.com/aerogo/flow v0.1.2/go.mod h1:xXIb7GY0AKouhbp4/ViCsiOmvvgRGonqPG30d6FnACI=
github.com/aerogo/flow v0.1.4 h1:BCb+nF58tj41xx3aJcxCLsNcGzZH6CGrodRMOIXYfOE=
github.com/aerogo/flow v0.1.4/go.mod h1:xXIb7GY0AKouhbp4/ViCsiOmvvgRGonqPG30d6FnACI=
github.com/aerogo/graphql v0.3.6 h1:6rAM4yFXa0qLaNz7bnLja3JAyV5fu2kq833Nsyzjizo=
github.com/aerogo/graphql v0.3.6/go.mod h1:bcAaQk3IGODFLF2gFwVszP3hyumWOhwKKuOh1xClSeI=
github.com/aerogo/http v1.0.1 h1:KTzYarhp5yougurxuAhJKFh3YvZ7R7CfaDVwJv5+xIs=
github.com/aerogo/http v1.0.1/go.mod h1:B1igUmMLpE6KabMpc9reHCJJNUOJ2U/PR9s1fF3TpPQ=
github.com/aerogo/graphql v0.4.0 h1:b5OyqzMNdskH6cKxRCLG6hKEpNYKCpxuad3tLjodox8=
github.com/aerogo/graphql v0.4.0/go.mod h1:yAY/KPoBejdN41JUGyK50pPZexFcGp//UMk/GjxqjZc=
github.com/aerogo/http v1.0.3 h1:vf6A+Igme5OHQPaP3a00uPDS0oxsx3puMA23d1NsWDM=
github.com/aerogo/http v1.0.3/go.mod h1:B1igUmMLpE6KabMpc9reHCJJNUOJ2U/PR9s1fF3TpPQ=
github.com/aerogo/http v1.0.6 h1:+aswlcWlUxjVcokF8hUjNJmGIEZuhbFbHi8uSadEvtc=
github.com/aerogo/http v1.0.6/go.mod h1:LwJ7b+LjrHj60FhYQ586K3/O7aNGxkE2dy/exEkQ6rA=
github.com/aerogo/layout v0.1.9 h1:pLGj1RaQgduDdhnRKoc40D5Mq4wINohQe08m7+TxTXA=
github.com/aerogo/layout v0.1.9/go.mod h1:cJ9Gyh0H9xHI2uVyPqSDIlpMDdgAosdYlRArEEh9oME=
github.com/aerogo/linter-performance v1.0.1 h1:/1Hak+7sxpO2AkjdMVcpkyurmA4YD77EDi7FRnLqnwM=
github.com/aerogo/linter-performance v1.0.1/go.mod h1:3ZXxPgQ0cEo0x6DCGZwXHrwKNYK86XvB5QRJR9s7Z6I=
github.com/aerogo/linter-performance v1.0.3 h1:pYsmUd8jp6CVrFx+YNo9Gfdf222CKG2gCVjp8cljZNY=
github.com/aerogo/linter-performance v1.0.3/go.mod h1:po6XSSbSgR30lazzqSRGV++a2omxYr2qjqFvcvUCH40=
github.com/aerogo/log v0.2.5 h1:LGeElbLqyaD8r8Ls9HuG7tYF6YV4kP56IxJWl/b4cZQ=
@ -61,54 +41,28 @@ github.com/aerogo/manifest v0.1.4 h1:JGRMJAANtgzhygMCMov6WgIRkiVuMgP3a+ossf//TJU
github.com/aerogo/manifest v0.1.4/go.mod h1:3SvBzx0rCDNQ+C779aEj5ZyP0YWwdGPeEzsPM3VIOzg=
github.com/aerogo/markdown v0.1.8 h1:X/FlyuBqdVaFflggxDbXcqGCQNInLKwU/tvyNhg+mQw=
github.com/aerogo/markdown v0.1.8/go.mod h1:8+ZWJnLT2rxY75TEd/Y5+rV89Sw9NOh5WiGSjptGNYk=
github.com/aerogo/mirror v0.2.2 h1:kASC9ZsTDBPwzZehb2o5qLbNv+bj8t9V167ExjEMRS0=
github.com/aerogo/mirror v0.2.2/go.mod h1:Un87Jq8RIRrb2bU1CxVToJjVZgSMLUQXxVLCXln4rUU=
github.com/aerogo/mirror v0.2.3 h1:mV4Bse2o2HiRN7rNntE6mOtWQLiLgTLkIrlt9vUoPD8=
github.com/aerogo/mirror v0.2.3/go.mod h1:Un87Jq8RIRrb2bU1CxVToJjVZgSMLUQXxVLCXln4rUU=
github.com/aerogo/nano v0.2.0 h1:qcCdCsAtN1Qpw8DhZQXiLVJYsjmpQMmZ7TPFD2GkwVw=
github.com/aerogo/nano v0.2.0/go.mod h1:tbR/fVGJb2rRFdPp9+D0INJgkhzQ+9XKefLTI11xNFU=
github.com/aerogo/nano v0.2.1 h1:YDGytzYoswid2x8cR5yAc9TmjgR+wEMF8+HowR1UCKI=
github.com/aerogo/nano v0.2.1/go.mod h1:1xgPREJH6aW5sE91AwYkvxMDg5YCbAaIYpAL7T1+JgA=
github.com/aerogo/nano v0.3.2 h1:1JWaZX8e+dtKZuPWC7Ls1AuzN33auCw7hPjZ8JCjjjE=
github.com/aerogo/nano v0.3.2/go.mod h1:o86DA68cpXx5inh8tygTJBF+8sXQ0eoOm36NHIN86YI=
github.com/aerogo/pack v0.5.0 h1:0BQVGNaPTIV4fQ5YG17eqOoe30aqpZp1NXCZxxRXxrA=
github.com/aerogo/pack v0.5.0/go.mod h1:TBMV1rF4ZO7qafslhLKCGbKsAGTbKPuCB5KpOUYW7ks=
github.com/aerogo/packet v0.1.4 h1:435YxvxMiH1KaBPELU+jCLqj0T9noLBSY4zk53t1gpQ=
github.com/aerogo/packet v0.1.4/go.mod h1:/t25yF9WG8B5/QB7wTiHLqwCEQ+nAze7uNm/JJNDQbQ=
github.com/aerogo/packet v0.1.5 h1:+J9VyVGlXnmaynukktwc7d+/nGxX7hBSNSj/7X8tg90=
github.com/aerogo/packet v0.1.5/go.mod h1:/t25yF9WG8B5/QB7wTiHLqwCEQ+nAze7uNm/JJNDQbQ=
github.com/aerogo/packet v0.1.6 h1:P9TwEV9xcyWxsjQQAXDfM3jItX4xEqRFNGXWMqH9j3Q=
github.com/aerogo/packet v0.1.6/go.mod h1:/t25yF9WG8B5/QB7wTiHLqwCEQ+nAze7uNm/JJNDQbQ=
github.com/aerogo/packet v0.1.7 h1:k/gBdAX/yecGW2xWJXt6dpg4Jb84xZScRPExt2rW+hY=
github.com/aerogo/packet v0.1.7/go.mod h1:/t25yF9WG8B5/QB7wTiHLqwCEQ+nAze7uNm/JJNDQbQ=
github.com/aerogo/pixy v1.2.4 h1:4VC9GmxSZALbaDKk5sd7hz2du/VhI2Tk2946PIE0k2I=
github.com/aerogo/pixy v1.2.4/go.mod h1:+8WIzngckOb4UDSRAgGY/CZX0lpZPFTbfLqOdJDIBC8=
github.com/aerogo/run v1.0.1 h1:IjvFUWo/97srJUIzAkETpj6e/bd9KcweRSQJF8vPJTs=
github.com/aerogo/run v1.0.1/go.mod h1:z4kZn3pRRiOd38DaFcMnLc5kR0coyGUFiIjH4vLCVNI=
github.com/aerogo/scarlet v0.2.4 h1:QSIHMuUKg2oW5V9bVvOsSC6QxND6B8AxxCEjFqiHPuU=
github.com/aerogo/scarlet v0.2.4/go.mod h1:0bIQypPu/IcSOAV7YtMXt6fhvxCaChDb6YI40/HqThI=
github.com/aerogo/session v0.1.2 h1:f0X3YJQJcFhtmw47U60jdSsH18/T51SkhkhAuXwnYI8=
github.com/aerogo/session v0.1.2/go.mod h1:A4S5dvAhpm+DRdQbno5Rn4Jvj0UVGJmOUKQXataNIDg=
github.com/aerogo/session v0.1.3 h1:ABE2DP0Cp92sqZDe/GkcbqjuZbYq+OV9Y9kpmZtchTw=
github.com/aerogo/session v0.1.3/go.mod h1:A4S5dvAhpm+DRdQbno5Rn4Jvj0UVGJmOUKQXataNIDg=
github.com/aerogo/session v0.1.4 h1:4OgQyUm3wxSsjNRReZhYdHX8X5lXnFp1W+M3EXr4a3E=
github.com/aerogo/session v0.1.4/go.mod h1:aM3FUclBU+wt8kwSs3leTByuaBu0sXrw4P+HwKVkSSw=
github.com/aerogo/session-store-memory v0.1.3 h1:6tPSh4HrJald0akOQIdUc8S1MxmBaOs1eXgC9Yiz2co=
github.com/aerogo/session-store-memory v0.1.3/go.mod h1:GYiLJNk8h3pcBB/UwfCJrtTZl1CLxBg8xnzt45wPmIM=
github.com/aerogo/session-store-memory v0.1.4 h1:nqeTEUtNZtwExVqo8HWOEUgIJKt5tQlWg1kG/gjtmQE=
github.com/aerogo/session-store-memory v0.1.4/go.mod h1:GYiLJNk8h3pcBB/UwfCJrtTZl1CLxBg8xnzt45wPmIM=
github.com/aerogo/session-store-memory v0.1.5 h1:l4uBDs4cVkByTE1N/IVSpU6oIrgx+4abtjztxk13zcI=
github.com/aerogo/session-store-memory v0.1.5/go.mod h1:Agy7YibR8yxUePqcl2rFyIGIwvXJiNsV1yaLtJHnnrI=
github.com/aerogo/session-store-nano v0.1.5 h1:klabo+Hyh1GKhpYJExdGvXDxNCxW7sSJevFGGzIqVCo=
github.com/aerogo/session-store-nano v0.1.5/go.mod h1:HEm80cLszsfh7yUsX1zbddznPhz2sPWhsHRXNLo9tJs=
github.com/aerogo/sitemap v0.1.2 h1:0+o/B0IrikZxGdi0yjgwa/jm36OMdOHxe6zChj8zGZA=
github.com/aerogo/sitemap v0.1.2/go.mod h1:/1NT13qIsTm/ydlZHEMd8m014E2yyQkI5coimmIfqc0=
github.com/akyoto/autoimport v0.6.3 h1:YpO9UqPr0GqHiD4c5qFKicTOA2a+yzudk2gs5yqpzBw=
github.com/akyoto/autoimport v0.6.3/go.mod h1:BKSPRNRNHYGGpoBsE/CfhV/ZljNDDFLvTRrmngMAZ30=
github.com/aerogo/sitemap v0.1.3 h1:HjiyvMCFvoB8a8Lm1+3LaA6B5EeP/f5CChrsAYjAFSQ=
github.com/aerogo/sitemap v0.1.3/go.mod h1:/1NT13qIsTm/ydlZHEMd8m014E2yyQkI5coimmIfqc0=
github.com/akyoto/cache v1.0.2 h1:YNaLbfbfBRr21dReBErQxfnVPBsVR/aeFpIKxsF88n8=
github.com/akyoto/cache v1.0.2/go.mod h1:MgYroBUaHREY9mmTcavctH4NDzQohCr4WMWPUKv7pq4=
github.com/akyoto/color v1.8.2 h1:FOTpvfQkCk00yCl8oU/wjO3+jBi3FWpwUogLkeyzWfU=
github.com/akyoto/color v1.8.2/go.mod h1:wiwOfYJb0XdHYznfIes7wjr79A/EjGPZ64FfbwJv4RY=
github.com/akyoto/color v1.8.3 h1:d+xQM5ra9aCxUzchbxSX2Szkd7cQwuJARSQMzNb3aLM=
github.com/akyoto/color v1.8.3/go.mod h1:wiwOfYJb0XdHYznfIes7wjr79A/EjGPZ64FfbwJv4RY=
github.com/akyoto/color v1.8.4 h1:XS1AL8/JmDHNgN9JRMd1epsQADQF7UXMzUz0nx0kTvY=
@ -117,16 +71,10 @@ github.com/akyoto/color v1.8.5 h1:xvHRmBJdsT5HJxnfyIT8l3zNbexA/2YIIeAlaHuhYGY=
github.com/akyoto/color v1.8.5/go.mod h1:mI8lhoKcgnvH8fnaupVUE3BmKUp2bpDn5wFS1xEQ4hw=
github.com/akyoto/go-matroska v0.1.1 h1:HgoCAkeWrGjYr0FZr3yCzAIkXuOGRiVil7Ul329lm+A=
github.com/akyoto/go-matroska v0.1.1/go.mod h1:x+GUVwyby6HN/MKKNP4BvGqP9VrHuEznfBf288gehek=
github.com/akyoto/hash v0.3.2 h1:T6nwsswtzXUjk5pBOLSDdj/8m9hcZzVzj35qnfG24aA=
github.com/akyoto/hash v0.3.2/go.mod h1:t6CqUCKqLvd1QfbNSt9yfv9BiOY+qPsZABimD86fNPY=
github.com/akyoto/hash v0.3.3 h1:zOQ71sRCRU56beF148dWUaCmWWpuLmi3dBd1Jiy8vHE=
github.com/akyoto/hash v0.3.3/go.mod h1:uPmnZyhBJIyLON8V9LNi0CcqtwYaH2RiKLFQg67fwq0=
github.com/akyoto/ignore v1.0.2 h1:A9hy/aUjaiKfjci0OPvwvme5F0e3PHrN9bjNn9oASkM=
github.com/akyoto/ignore v1.0.2/go.mod h1:GhkaVB2bVMq7KuV9AkB1U6FGWZ7mTpuXeKVQNEWVUc0=
github.com/akyoto/hash v0.3.5 h1:5EJGHx6RfE9aHrEzWU3pfLGFUWMvPVqtsxt7mSON+mY=
github.com/akyoto/hash v0.3.5/go.mod h1:uPmnZyhBJIyLON8V9LNi0CcqtwYaH2RiKLFQg67fwq0=
github.com/akyoto/imageserver v0.3.6 h1:Sxcbgo45Lh7afcSmcU8OS49VYbqh4kE3DK0Lxuuxf74=
github.com/akyoto/imageserver v0.3.6/go.mod h1:9AuMUxIt5CPlTmJre4ETwWxRnThOkkE1EhavC8HX4U8=
github.com/akyoto/stringutils v0.1.0 h1:MuRXgxETSPgkrT+j3L9/oEqHs2/yW/gr2ioq3j/5LtI=
github.com/akyoto/stringutils v0.1.0/go.mod h1:a83vMdYtgvZD3nuWJIW5kzbrjv5dHH3Qg6ucbF1Z/k4=
github.com/akyoto/stringutils v0.2.0 h1:86gMW/31LO7zc8mGGWB4gLqyFzrNHMZYyj6ebSQbpH4=
github.com/akyoto/stringutils v0.2.0/go.mod h1:EigXf5ZaULP6f5CNjqIIo0fjDHbRe8P4BQwlqCsvi9k=
github.com/akyoto/stringutils v0.2.1 h1:4St8vcRnNFuCNSMEJ34TeLb7/FfsvZaSU8/Scr6Zymg=
@ -137,8 +85,8 @@ github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRy
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/animenotifier/anilist v0.2.3 h1:409h1m4m59EBTQHc/F2U5PGY3lIWlvD/kRXxY1oTl5Q=
github.com/animenotifier/anilist v0.2.3/go.mod h1:WmivLHBTIs+zmqENjiVXH66laTYB8vT5d+8q1yzLX9I=
github.com/animenotifier/arn v1.1.25 h1:RyAMLdOVQrGto5A+RSLgs/OrnTy5WSyql559YkyPvUI=
github.com/animenotifier/arn v1.1.25/go.mod h1:qf9Uq5lsw3uIgFFrfOSu1L9hjtHer9VcmgIWWcuN/Jo=
github.com/animenotifier/arn v1.2.0 h1:BsWB6CXUFRgPKeh8MtNtgi8TPwDoqpAkgV8ah+bDTR8=
github.com/animenotifier/arn v1.2.0/go.mod h1:1wyusZbu0AbbjPn60TR20xwL8alLPo41oysxsttEP2A=
github.com/animenotifier/ffxiv v0.2.1 h1:gV5h47skizAWLJQb+M3CmExy1hlqDuKmNxkOpn3JwF0=
github.com/animenotifier/ffxiv v0.2.1/go.mod h1:9p0z9iQIT8nIlwH4xHUvdo0qFvJ4pVnFbBQ0G/JiY0k=
github.com/animenotifier/japanese v0.2.3 h1:fGX3CcX5lGzRC+JkokDCwJqRniPOmM44FLm7aqdnOEo=
@ -205,8 +153,6 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -214,8 +160,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/logpacker/PayPal-Go-SDK v1.1.4 h1:YXfHvkDLMKkdmHmeusBum45MMG4n3iJeeQ9mZPWPAUQ=
github.com/logpacker/PayPal-Go-SDK v1.1.4/go.mod h1:DUf5ncyG0n3jFnU9VsuQqe/Vo6KHtWQ6HYf80JwP4rY=
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
@ -241,11 +185,8 @@ github.com/mssola/user_agent v0.5.0 h1:gRF7/x8cKt8qzAosYGsBNyirta+F8fvYDlJrgXws9
github.com/mssola/user_agent v0.5.0/go.mod h1:UFiKPVaShrJGW93n4uo8dpPdg1BSVpw2P9bneo0Mtp8=
github.com/pariz/gountries v0.0.0-20171019111738-adb00f6513a3 h1:lmQNznFSupyfCDE9d7wdKOU8UiVwWEoYwv8wo6rSgy0=
github.com/pariz/gountries v0.0.0-20171019111738-adb00f6513a3/go.mod h1:U0ETmPPEsfd7CpUKNMYi68xIOL8Ww4jPZlaqNngcwqs=
github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
@ -256,24 +197,20 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 h1:hBSHahWMEgzwRyS6dRpxY0XyjZsHyQ61s084wo5PJe0=
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
github.com/tdewolff/test v1.0.0 h1:jOwzqCXr5ePXEPGJaq2ivoR6HOCi+D5TPfpoyg8yvmU=
github.com/tdewolff/test v1.0.0/go.mod h1:DiQUlutnqlEvdvhSn2LPGy4TFwRauAaYDsL+683RNX4=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/ungerik/go-gravatar v0.0.0-20120802094239-6ab22628222a h1:TZyMbJbyPL+4/ndyXns8aNDrmUJn5a6aV8lj3qEM7fM=
github.com/ungerik/go-gravatar v0.0.0-20120802094239-6ab22628222a/go.mod h1:cmQAsXze586z5DHYfoVO9jZBampncP3iuhVgujPqdxk=
github.com/ventu-io/go-shortid v0.0.0-20171029131806-771a37caa5cf h1:cgAKVljim9RJRcJNGjnBUajXj1FupBSdWwW4JaQG7vk=
@ -285,8 +222,8 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -316,31 +253,23 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190428183149-804c0c7841b5 h1:m0i9YywO9THhxmJvLEwKJDD/pD8ljCB+EaT/wYS41Is=
golang.org/x/sys v0.0.0-20190428183149-804c0c7841b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438 h1:khxRGsvPk4n2y8I/mLLjp7e5dMTJmH75wvqS6nMwUtY=
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190516014833-cab07311ab81 h1:5Q88vZAfC0WB8T1GHRLttQaZdCNeQHM40n41gMUeFlI=
golang.org/x/sys v0.0.0-20190516014833-cab07311ab81/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190516102723-cedb8e16d18a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb h1:k07iPOt0d6nEnwXF+kHB+iEg+WSuKe/SOQuFM2QoD+E=
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5 h1:sM3evRHxE/1RuMe1FYAL3j7C7fUfIjkbE+NiDAYUF8U=
golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09 h1:IlD35wZE03o2qJy2o37WIskL33b7PT6cHdGnE8bieZs=
golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 h1:T5DasATyLQfmbTpfEXx/IOL9vfjzW6up+ZDkmHvIf2s=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=

View File

@ -4,15 +4,16 @@ import (
"sort"
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/middleware"
"github.com/animenotifier/notify.moe/utils"
)
// Render layout.
func Render(ctx *aero.Context, content string) string {
func Render(ctx aero.Context, content string) string {
user := utils.GetUser(ctx)
openGraph, _ := ctx.Data.(*arn.OpenGraph)
customCtx := ctx.(*middleware.OpenGraphContext)
openGraph := customCtx.OpenGraph
// Make output order deterministic to profit from Aero caching.
// To do this, we need to create slices and sort the tags.

View File

@ -1,4 +1,4 @@
component Layout(ctx *aero.Context, user *arn.User, openGraph *arn.OpenGraph, meta, tags []string, content string)
component Layout(ctx aero.Context, user *arn.User, openGraph *arn.OpenGraph, meta, tags []string, content string)
html(lang="en")
head
link(rel="stylesheet", href="/styles", importance="high")
@ -6,7 +6,7 @@ component Layout(ctx *aero.Context, user *arn.User, openGraph *arn.OpenGraph, me
if openGraph != nil
title= openGraph.Tags["og:title"]
else
title= ctx.App.Config.Title
title= assets.Manifest.Name
//- Viewport
meta(name="viewport", content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes")

24
main.go
View File

@ -5,6 +5,7 @@ import (
"github.com/aerogo/aero"
nanostore "github.com/aerogo/session-store-nano"
"github.com/akyoto/color"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/auth"
@ -44,9 +45,10 @@ func configure(app *aero.Application) *aero.Application {
// Middleware
app.Use(
middleware.Log(),
middleware.Session(),
middleware.UserInfo(),
middleware.OpenGraph,
middleware.Log,
middleware.Session,
middleware.UserInfo,
)
// API
@ -54,9 +56,8 @@ func configure(app *aero.Application) *aero.Application {
// Development server configuration
if arn.IsDevelopment() {
app.Config.Domain = "beta.notify.moe"
app.Config.Title += " - Beta"
assets.Manifest.Name = app.Config.Title
assets.Domain = "beta.notify.moe"
assets.Manifest.Name += " - Beta"
}
// Authentication
@ -68,6 +69,11 @@ func configure(app *aero.Application) *aero.Application {
// Close the database node on shutdown
app.OnEnd(arn.Node.Close)
// Show errors in the console
app.OnError(func(ctx aero.Context, err error) {
color.Red(err.Error())
})
// Check that this is the server
if !arn.Node.IsServer() && !arn.IsTest() {
panic("Another program is currently running as the database server")
@ -77,13 +83,13 @@ func configure(app *aero.Application) *aero.Application {
arn.DB.Prefetch()
// Do not use HTTP/2 push on service worker requests
app.AddPushCondition(func(ctx *aero.Context) bool {
return !strings.Contains(ctx.Request().Header().Get("Referer"), "/service-worker")
app.AddPushCondition(func(ctx aero.Context) bool {
return !strings.Contains(ctx.Request().Header("Referer"), "/service-worker")
})
// Specify test routes
for route, examples := range routetests.All() {
app.Test(route, examples)
app.Test(route, examples...)
}
return app

View File

@ -112,7 +112,7 @@ func testRoute(t *testing.T, app *aero.Application, route string) {
// Record the response
responseRecorder := httptest.NewRecorder()
app.Handler().ServeHTTP(responseRecorder, request)
app.ServeHTTP(responseRecorder, request)
status := responseRecorder.Code
switch status {

View File

@ -47,9 +47,9 @@ test:
bench:
$(GOTEST) -bench .
pack:
go install github.com/aerogo/pack
go install github.com/aerogo/pack/...
run:
go install github.com/aerogo/run
go install github.com/aerogo/run/...
tools:
ifeq ($(OSNAME),OSX)
brew install coreutils

View File

@ -20,10 +20,10 @@ package middleware
// // Firewall middleware detects malicious requests.
// func Firewall() aero.Middleware {
// return func(ctx *aero.Context, next func()) {
// return func(ctx aero.Context, next func()) {
// var stats *IPStats
// ip := ctx.RealIP()
// ip := ctx.IP()
// // Allow localhost
// if ip == "127.0.0.1" {
@ -44,7 +44,7 @@ package middleware
// }
// // Add requested URI to the list of requests
// stats.Requests = append(stats.Requests, ctx.URI())
// stats.Requests = append(stats.Requests, ctx.Path())
// if len(stats.Requests) > requestThreshold {
// stats.Requests = stats.Requests[len(stats.Requests)-requestThreshold:]
@ -69,7 +69,7 @@ package middleware
// }
// // Disallow request
// request.Error("[guest]", ip, "BLOCKED BY FIREWALL", ctx.URI())
// request.Error("[guest]", ip, "BLOCKED BY FIREWALL", ctx.Path())
// return
// }

View File

@ -2,15 +2,15 @@ package middleware
// // HTTPSRedirect middleware redirects to HTTPS if needed.
// func HTTPSRedirect() aero.Middleware {
// return func(ctx *aero.Context, next func()) {
// return func(ctx aero.Context, next func()) {
// request := ctx.Request()
// userAgent := request.Header().Get("User-Agent")
// isBrowser := strings.Contains(userAgent, "Mozilla/") || strings.Contains(userAgent, "Chrome/") || strings.Contains(userAgent, "AppleWebKit/")
// if !strings.HasPrefix(request.Protocol(), "HTTP/2") && isBrowser {
// fmt.Println("Redirect to HTTPS")
// ctx.Redirect("https://" + request.Host() + request.URL().Path)
// ctx.Response().WriteHeader(ctx.StatusCode)
// ctx.Redirect(http.StatusFound, "https://" + request.Host() + request.URL().Path)
// ctx.Response().WriteHeader(ctx.Status())
// return
// }

View File

@ -32,18 +32,19 @@ func init() {
}
// Log middleware logs every request into logs/request.log and errors into logs/error.log.
func Log() aero.Middleware {
return func(ctx *aero.Context, next func()) {
func Log(next aero.Handler) aero.Handler {
return func(ctx aero.Context) error {
start := time.Now()
next()
err := next(ctx)
responseTime := time.Since(start)
go logRequest(ctx, responseTime)
return err
}
}
// Logs a single request
func logRequest(ctx *aero.Context, responseTime time.Duration) {
func logRequest(ctx aero.Context, responseTime time.Duration) {
responseTimeString := strconv.Itoa(int(responseTime.Nanoseconds()/1000000)) + " ms"
repeatSpaceCount := 8 - len(responseTimeString)
@ -54,7 +55,7 @@ func logRequest(ctx *aero.Context, responseTime time.Duration) {
responseTimeString = strings.Repeat(" ", repeatSpaceCount) + responseTimeString
user := utils.GetUser(ctx)
ip := ctx.RealIP()
ip := ctx.IP()
hostNames, cached := GetHostsForIP(ip)
if !cached && len(hostNames) > 0 {
@ -70,20 +71,20 @@ func logRequest(ctx *aero.Context, responseTime time.Duration) {
nick = user.Nick
}
requestLog.Info("%s | %s | %s | %s | %d | %s", nick, id, ip, responseTimeString, ctx.StatusCode, ctx.URI())
requestLog.Info("%s | %s | %s | %s | %d | %s", nick, id, ip, responseTimeString, ctx.Status(), ctx.Path())
// Log all requests that failed
switch ctx.StatusCode {
switch ctx.Status() {
case http.StatusOK, http.StatusFound, http.StatusMovedPermanently, http.StatusPermanentRedirect, http.StatusTemporaryRedirect:
// Ok.
default:
errorLog.Error("%s | %s | %s | %s | %d | %s (%s)", nick, id, ip, responseTimeString, ctx.StatusCode, ctx.URI(), ctx.ErrorMessage)
errorLog.Error("%s | %s | %s | %s | %d | %s", nick, id, ip, responseTimeString, ctx.Status(), ctx.Path())
}
// Notify us about long requests.
// However ignore requests under /auth/ because those depend on 3rd party servers.
if responseTime >= 500*time.Millisecond && !strings.HasPrefix(ctx.URI(), "/auth/") && !strings.HasPrefix(ctx.URI(), "/sitemap/") && !strings.HasPrefix(ctx.URI(), "/api/sse/") {
errorLog.Error("%s | %s | %s | %s | %d | %s (long response time)", nick, id, ip, responseTimeString, ctx.StatusCode, ctx.URI())
if responseTime >= 500*time.Millisecond && !strings.HasPrefix(ctx.Path(), "/auth/") && !strings.HasPrefix(ctx.Path(), "/sitemap/") && !strings.HasPrefix(ctx.Path(), "/api/sse/") {
errorLog.Error("%s | %s | %s | %s | %d | %s (long response time)", nick, id, ip, responseTimeString, ctx.Status(), ctx.Path())
}
}

24
middleware/OpenGraph.go Normal file
View File

@ -0,0 +1,24 @@
package middleware
import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
)
// OpenGraphContext is a context with open graph data.
type OpenGraphContext struct {
aero.Context
*arn.OpenGraph
}
// OpenGraph middleware modifies the context to be an OpenGraphContext.
func OpenGraph(next aero.Handler) aero.Handler {
return func(ctx aero.Context) error {
ctx = &OpenGraphContext{
Context: ctx,
OpenGraph: nil,
}
return next(ctx)
}
}

34
middleware/Recover.go Normal file
View File

@ -0,0 +1,34 @@
package middleware
import (
"fmt"
"net/http"
"runtime"
"github.com/aerogo/aero"
)
// Recover recovers from panics and shows them as the response body.
func Recover(next aero.Handler) aero.Handler {
return func(ctx aero.Context) error {
defer func() {
r := recover()
if r == nil {
return
}
err, ok := r.(error)
if !ok {
err = fmt.Errorf("%v", r)
}
stack := make([]byte, 4096)
length := runtime.Stack(stack, true)
_ = ctx.Error(http.StatusInternalServerError, err, stack[:length])
}()
return next(ctx)
}
}

View File

@ -3,14 +3,16 @@ package middleware
import "github.com/aerogo/aero"
// Session middleware saves an existing session if it has been modified.
func Session() aero.Middleware {
return func(ctx *aero.Context, next func()) {
func Session(next aero.Handler) aero.Handler {
return func(ctx aero.Context) error {
// Handle the request first
next()
err := next(ctx)
// Update session if it has been modified
if ctx.HasSession() && ctx.Session().Modified() {
ctx.App.Sessions.Store.Set(ctx.Session().ID(), ctx.Session())
_ = ctx.App().Sessions.Store.Set(ctx.Session().ID(), ctx.Session())
}
return err
}
}

View File

@ -14,33 +14,35 @@ import (
)
// UserInfo updates user related information after each request.
func UserInfo() aero.Middleware {
return func(ctx *aero.Context, next func()) {
next()
func UserInfo(next aero.Handler) aero.Handler {
return func(ctx aero.Context) error {
err := next(ctx)
// Ignore non-HTML requests
contentType := ctx.Response().Header().Get("Content-Type")
contentType := ctx.Response().Header("Content-Type")
if !strings.HasPrefix(contentType, "text/html") {
return
return nil
}
user := utils.GetUser(ctx)
// When there's no user logged in, nothing to update
if user == nil {
return
return nil
}
// This works asynchronously so it doesn't block the response
go updateUserInfo(ctx, user)
return err
}
}
// Update browser and OS data
func updateUserInfo(ctx *aero.Context, user *arn.User) {
newIP := ctx.RealIP()
newUserAgent := ctx.UserAgent()
func updateUserInfo(ctx aero.Context, user *arn.User) {
newIP := ctx.IP()
newUserAgent := ctx.Request().Header("User-Agent")
if user.UserAgent != newUserAgent {
user.UserAgent = newUserAgent
@ -91,7 +93,12 @@ func updateUserLocation(user *arn.User, newIP string) {
}
newLocation := arn.IPInfoDBLocation{}
response.Unmarshal(&newLocation)
err = response.Unmarshal(&newLocation)
if err != nil {
color.Red("Couldn't deserialize location data | Status: %d | IP: %s", response.StatusCode, user.IP)
return
}
if newLocation.CountryName != "-" {
user.Location.CountryName = newLocation.CountryName

View File

@ -7,14 +7,14 @@ import (
)
// Global activity page.
func Global(ctx *aero.Context) string {
func Global(ctx aero.Context) error {
user := utils.GetUser(ctx)
activities := fetchActivities(user, false)
return render(ctx, activities)
}
// Followed activity page.
func Followed(ctx *aero.Context) string {
func Followed(ctx aero.Context) error {
user := utils.GetUser(ctx)
activities := fetchActivities(user, true)
return render(ctx, activities)

View File

@ -14,7 +14,7 @@ const (
)
// render renders the activities page with the given activities.
func render(ctx *aero.Context, allActivities []arn.Activity) string {
func render(ctx aero.Context, allActivities []arn.Activity) error {
user := utils.GetUser(ctx)
index, _ := ctx.GetInt("index")

View File

@ -1,6 +1,7 @@
package admin
import (
"net/http"
"runtime"
"strings"
@ -14,11 +15,11 @@ import (
)
// Get admin page.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil || (user.Role != "admin" && user.Role != "editor") {
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
// // CPU

View File

@ -11,7 +11,7 @@ import (
const maxReports = 80
// ClientErrors shows client-side errors.
func ClientErrors(ctx *aero.Context) string {
func ClientErrors(ctx aero.Context) error {
reports := arn.AllClientErrorReports()
sort.Slice(reports, func(i, j int) bool {

View File

@ -11,7 +11,7 @@ import (
)
// PaymentHistory ...
func PaymentHistory(ctx *aero.Context) string {
func PaymentHistory(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil {

View File

@ -11,7 +11,7 @@ import (
)
// PurchaseHistory ...
func PurchaseHistory(ctx *aero.Context) string {
func PurchaseHistory(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil {

View File

@ -12,7 +12,7 @@ import (
)
// UserRegistrations ...
func UserRegistrations(ctx *aero.Context) string {
func UserRegistrations(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil {

View File

@ -4,6 +4,6 @@ import "github.com/aerogo/aero"
import "github.com/animenotifier/notify.moe/components"
// WebDev ...
func WebDev(ctx *aero.Context) string {
func WebDev(ctx aero.Context) error {
return ctx.HTML(components.WebDev())
}

View File

@ -6,11 +6,12 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/middleware"
"github.com/animenotifier/notify.moe/utils"
)
// Get a single AMV.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
id := ctx.Get("id")
amv, err := arn.GetAMV(id)
user := utils.GetUser(ctx)
@ -19,6 +20,7 @@ func Get(ctx *aero.Context) string {
return ctx.Error(http.StatusNotFound, "AMV not found", err)
}
ctx.Data = getOpenGraph(ctx, amv)
customCtx := ctx.(*middleware.OpenGraphContext)
customCtx.OpenGraph = getOpenGraph(ctx, amv)
return ctx.HTML(components.AMVPage(amv, user))
}

View File

@ -11,7 +11,7 @@ import (
)
// Edit track.
func Edit(ctx *aero.Context) string {
func Edit(ctx aero.Context) error {
id := ctx.Get("id")
amv, err := arn.GetAMV(id)
user := utils.GetUser(ctx)

View File

@ -5,14 +5,15 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
)
func getOpenGraph(ctx *aero.Context, amv *arn.AMV) *arn.OpenGraph {
func getOpenGraph(ctx aero.Context, amv *arn.AMV) *arn.OpenGraph {
openGraph := &arn.OpenGraph{
Tags: map[string]string{
"og:title": amv.Title.ByUser(nil) + " (AMV)",
"og:url": "https://" + ctx.App.Config.Domain + amv.Link(),
"og:site_name": ctx.App.Config.Domain,
"og:url": "https://" + assets.Domain + amv.Link(),
"og:site_name": assets.Domain,
"og:type": "video.other",
},
Meta: map[string]string{},
@ -21,7 +22,7 @@ func getOpenGraph(ctx *aero.Context, amv *arn.AMV) *arn.OpenGraph {
openGraph.Tags["og:description"] = strings.Join(amv.Tags, ", ")
if amv.File != "" {
openGraph.Tags["og:video"] = "https://" + ctx.App.Config.Domain + "/videos/amvs/" + amv.File
openGraph.Tags["og:video"] = "https://" + assets.Domain + "/videos/amvs/" + amv.File
openGraph.Tags["og:video:type"] = "video/webm"
openGraph.Tags["og:video:width"] = "640"
openGraph.Tags["og:video:height"] = "360"

View File

@ -7,7 +7,7 @@ import (
)
// Best AMVs.
func Best(ctx *aero.Context) string {
func Best(ctx aero.Context) error {
amvs := fetchAll()
sort.Slice(amvs, func(i, j int) bool {

View File

@ -7,7 +7,7 @@ import (
)
// Latest AMVs.
func Latest(ctx *aero.Context) string {
func Latest(ctx aero.Context) error {
amvs := fetchAll()
sort.Slice(amvs, func(i, j int) bool {

View File

@ -14,7 +14,7 @@ const (
)
// render renders the AMVs page with the given AMVs.
func render(ctx *aero.Context, allAMVs []*arn.AMV) string {
func render(ctx aero.Context, allAMVs []*arn.AMV) error {
user := utils.GetUser(ctx)
index, _ := ctx.GetInt("index")
tag := ctx.Get("tag")

View File

@ -6,7 +6,9 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/middleware"
"github.com/animenotifier/notify.moe/utils"
)
@ -18,7 +20,7 @@ const (
)
// Get anime page.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)
anime, err := arn.GetAnime(id)
@ -120,12 +122,13 @@ func Get(ctx *aero.Context) string {
})
// Open Graph
ctx.Data = getOpenGraph(ctx, anime)
customCtx := ctx.(*middleware.OpenGraphContext)
customCtx.OpenGraph = getOpenGraph(ctx, anime)
return ctx.HTML(components.Anime(anime, animeListItem, tracks, amvs, amvAppearances, episodes, friends, friendsAnimeListItems, episodeToFriends, user))
}
func getOpenGraph(ctx *aero.Context, anime *arn.Anime) *arn.OpenGraph {
func getOpenGraph(ctx aero.Context, anime *arn.Anime) *arn.OpenGraph {
description := anime.Summary
if len(description) > maxDescriptionLength {
@ -136,7 +139,7 @@ func getOpenGraph(ctx *aero.Context, anime *arn.Anime) *arn.OpenGraph {
Tags: map[string]string{
"og:title": anime.Title.Canonical,
"og:image": "https:" + anime.ImageLink("large"),
"og:url": "https://" + ctx.App.Config.Domain + anime.Link(),
"og:url": "https://" + assets.Domain + anime.Link(),
"og:site_name": "notify.moe",
"og:description": description,
},

View File

@ -12,7 +12,7 @@ import (
)
// Characters ...
func Characters(ctx *aero.Context) string {
func Characters(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)
anime, err := arn.GetAnime(id)

View File

@ -11,7 +11,7 @@ import (
)
// Comments ...
func Comments(ctx *aero.Context) string {
func Comments(ctx aero.Context) error {
user := utils.GetUser(ctx)
id := ctx.Get("id")
anime, err := arn.GetAnime(id)

View File

@ -11,7 +11,7 @@ import (
)
// Main anime edit page.
func Main(ctx *aero.Context) string {
func Main(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)
@ -29,7 +29,7 @@ func Main(ctx *aero.Context) string {
}
// Images anime images edit page.
func Images(ctx *aero.Context) string {
func Images(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)
@ -47,7 +47,7 @@ func Images(ctx *aero.Context) string {
}
// Characters anime characters edit page.
func Characters(ctx *aero.Context) string {
func Characters(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)
@ -71,7 +71,7 @@ func Characters(ctx *aero.Context) string {
}
// Relations anime relations edit page.
func Relations(ctx *aero.Context) string {
func Relations(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)
@ -95,7 +95,7 @@ func Relations(ctx *aero.Context) string {
}
// Episodes anime episodes edit page.
func Episodes(ctx *aero.Context) string {
func Episodes(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)

View File

@ -11,7 +11,7 @@ import (
)
// Episodes ...
func Episodes(ctx *aero.Context) string {
func Episodes(ctx aero.Context) error {
user := utils.GetUser(ctx)
id := ctx.Get("id")
anime, err := arn.GetAnime(id)

View File

@ -9,8 +9,8 @@ import (
)
// RedirectByMapping redirects to the anime with the given mapping ID.
func RedirectByMapping(mappingName string) func(*aero.Context) string {
return func(ctx *aero.Context) string {
func RedirectByMapping(mappingName string) func(aero.Context) error {
return func(ctx aero.Context) error {
id := ctx.Get("id")
finder := arn.NewAnimeFinder(mappingName)
anime := finder.GetAnime(id)

View File

@ -10,7 +10,7 @@ import (
)
// Relations ...
func Relations(ctx *aero.Context) string {
func Relations(ctx aero.Context) error {
user := utils.GetUser(ctx)
id := ctx.Get("id")

View File

@ -12,7 +12,7 @@ import (
)
// Tracks ...
func Tracks(ctx *aero.Context) string {
func Tracks(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)

View File

@ -10,7 +10,7 @@ import (
)
// DeleteKitsu marks an anime for deletion.
func DeleteKitsu(ctx *aero.Context) string {
func DeleteKitsu(ctx aero.Context) error {
id := ctx.Get("id")
// Is the user allowed to delete?
@ -39,5 +39,5 @@ func DeleteKitsu(ctx *aero.Context) string {
// Save in database
arn.DB.Set("IDList", "deleted kitsu anime", &deletedKitsuAnime)
return ""
return nil
}

View File

@ -14,7 +14,7 @@ import (
)
// Kitsu anime import.
func Kitsu(ctx *aero.Context) string {
func Kitsu(ctx aero.Context) error {
id := ctx.Get("id")
user := utils.GetUser(ctx)
@ -45,5 +45,5 @@ func Kitsu(ctx *aero.Context) string {
// Log
fmt.Println(color.GreenString("✔"), anime.ID, anime.Title.Canonical)
return ""
return nil
}

View File

@ -6,7 +6,9 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/middleware"
"github.com/animenotifier/notify.moe/utils"
"github.com/animenotifier/notify.moe/utils/infinitescroll"
)
@ -17,15 +19,15 @@ const (
)
// FilterByStatus returns a handler for the given anime list item status.
func FilterByStatus(status string) aero.Handle {
return func(ctx *aero.Context) string {
func FilterByStatus(status string) aero.Handler {
return func(ctx aero.Context) error {
user := utils.GetUser(ctx)
return AnimeList(ctx, user, status)
}
}
// AnimeList renders the anime list items.
func AnimeList(ctx *aero.Context, user *arn.User, status string) string {
func AnimeList(ctx aero.Context, user *arn.User, status string) error {
nick := ctx.Get("nick")
index, _ := ctx.GetInt("index")
viewUser, err := arn.GetUserByNick(nick)
@ -70,11 +72,12 @@ func AnimeList(ctx *aero.Context, user *arn.User, status string) string {
nextIndex := infinitescroll.NextIndex(ctx, len(allItems), maxLength, index)
// OpenGraph data
ctx.Data = &arn.OpenGraph{
customCtx := ctx.(*middleware.OpenGraphContext)
customCtx.OpenGraph = &arn.OpenGraph{
Tags: map[string]string{
"og:title": viewUser.Nick + "'s anime list",
"og:image": "https:" + viewUser.AvatarLink("large"),
"og:url": "https://" + ctx.App.Config.Domain + viewUser.Link(),
"og:url": "https://" + assets.Domain + viewUser.Link(),
"og:site_name": "notify.moe",
"og:description": strconv.Itoa(len(animeList.Items)) + " anime",

View File

@ -8,12 +8,12 @@ import (
)
// Redirect to the full URL including the user nick.
func Redirect(ctx *aero.Context) string {
func Redirect(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil {
return ctx.Error(http.StatusUnauthorized, "Not logged in")
}
return ctx.Redirect("/+" + user.Nick + ctx.URI())
return ctx.Redirect(http.StatusFound, "/+" + user.Nick + ctx.Path())
}

View File

@ -11,7 +11,7 @@ import (
)
// Get anime page.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
nick := ctx.Get("nick")
viewUser, err := arn.GetUserByNick(nick)

View File

@ -12,7 +12,7 @@ import (
)
// Get api page.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
types := []*autodocs.Type{}
for typeName := range arn.DB.Types() {

View File

@ -13,8 +13,8 @@ import (
)
// ByType renders the api docs page for the given type.
func ByType(typeName string) func(*aero.Context) string {
return func(ctx *aero.Context) string {
func ByType(typeName string) func(aero.Context) error {
return func(ctx aero.Context) error {
t := arn.API.Type(typeName)
fields := []*utils.APIField{}

View File

@ -22,7 +22,7 @@ var weekdayNames = []string{
}
// Get ...
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
oneWeek := 7 * 24 * time.Hour

View File

@ -7,7 +7,9 @@ import (
"github.com/aerogo/aero"
"github.com/akyoto/color"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/middleware"
"github.com/animenotifier/notify.moe/utils"
)
@ -16,7 +18,7 @@ const (
)
// Get character.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
id := ctx.Get("id")
character, err := arn.GetCharacter(id)
@ -101,11 +103,12 @@ func Get(ctx *aero.Context) string {
// Set OpenGraph attributes
description := utils.CutLongDescription(character.Description)
ctx.Data = &arn.OpenGraph{
customCtx := ctx.(*middleware.OpenGraphContext)
customCtx.OpenGraph = &arn.OpenGraph{
Tags: map[string]string{
"og:title": character.Name.Canonical,
"og:image": "https:" + character.ImageLink("large"),
"og:url": "https://" + ctx.App.Config.Domain + character.Link(),
"og:url": "https://" + assets.Domain + character.Link(),
"og:site_name": "notify.moe",
"og:description": description,

View File

@ -11,7 +11,7 @@ import (
)
// Edit character.
func Edit(ctx *aero.Context) string {
func Edit(ctx aero.Context) error {
id := ctx.Get("id")
character, err := arn.GetCharacter(id)
user := utils.GetUser(ctx)
@ -24,7 +24,7 @@ func Edit(ctx *aero.Context) string {
}
// EditImages renders the form to edit the character images.
func EditImages(ctx *aero.Context) string {
func EditImages(ctx aero.Context) error {
id := ctx.Get("id")
character, err := arn.GetCharacter(id)
user := utils.GetUser(ctx)

View File

@ -8,7 +8,7 @@ import (
)
// Ranking returns the ranking information for the character via the API.
func Ranking(ctx *aero.Context) string {
func Ranking(ctx aero.Context) error {
// Check character ID
id := ctx.Get("id")
_, err := arn.GetCharacter(id)
@ -35,7 +35,7 @@ func Ranking(ctx *aero.Context) string {
arn.SortCharactersByLikes(characters)
// Allow CORS
ctx.Response().Header().Set("Access-Control-Allow-Origin", "*")
ctx.Response().SetHeader("Access-Control-Allow-Origin", "*")
// Return ranking
for index, character := range characters {

View File

@ -6,7 +6,7 @@ import (
)
// Best characters.
func Best(ctx *aero.Context) string {
func Best(ctx aero.Context) error {
characters := fetchAll()
arn.SortCharactersByLikes(characters)

View File

@ -7,7 +7,7 @@ import (
)
// Latest characters.
func Latest(ctx *aero.Context) string {
func Latest(ctx aero.Context) error {
characters := fetchAll()
sort.Slice(characters, func(i, j int) bool {

View File

@ -14,7 +14,7 @@ const (
)
// render renders the characters page with the given characters.
func render(ctx *aero.Context, allCharacters []*arn.Character) string {
func render(ctx aero.Context, allCharacters []*arn.Character) error {
user := utils.GetUser(ctx)
index, _ := ctx.GetInt("index")
tag := ctx.Get("tag")

View File

@ -10,7 +10,7 @@ import (
)
// Get charge page.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil {

View File

@ -12,7 +12,7 @@ import (
)
// All renders an index of all companies.
func All(ctx *aero.Context) string {
func All(ctx aero.Context) error {
user := utils.GetUser(ctx)
companies := arn.FilterCompanies(func(company *arn.Company) bool {

View File

@ -11,7 +11,7 @@ import (
const maxPopularCompanies = 10
// Popular renders the best companies.
func Popular(ctx *aero.Context) string {
func Popular(ctx aero.Context) error {
user := utils.GetUser(ctx)
index, _ := ctx.GetInt("index")

View File

@ -6,12 +6,14 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/middleware"
"github.com/animenotifier/notify.moe/utils"
)
// Get renders a company page.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
id := ctx.Get("id")
company, err := arn.GetCompany(id)
@ -25,7 +27,7 @@ func Get(ctx *aero.Context) string {
openGraph := &arn.OpenGraph{
Tags: map[string]string{
"og:title": company.Name.English,
"og:url": "https://" + ctx.App.Config.Domain + company.Link(),
"og:url": "https://" + assets.Domain + company.Link(),
"og:site_name": "notify.moe",
"og:type": "article",
},
@ -41,7 +43,8 @@ func Get(ctx *aero.Context) string {
openGraph.Tags["og:description"] = company.Name.English + " company information."
}
ctx.Data = openGraph
customCtx := ctx.(*middleware.OpenGraphContext)
customCtx.OpenGraph = openGraph
studioAnime, producedAnime, licensedAnime := company.Anime()

View File

@ -5,13 +5,15 @@ import (
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/assets"
"github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/middleware"
"github.com/animenotifier/notify.moe/utils"
"github.com/animenotifier/notify.moe/utils/editform"
)
// Edit company.
func Edit(ctx *aero.Context) string {
func Edit(ctx aero.Context) error {
id := ctx.Get("id")
company, err := arn.GetCompany(id)
user := utils.GetUser(ctx)
@ -20,10 +22,11 @@ func Edit(ctx *aero.Context) string {
return ctx.Error(http.StatusNotFound, "Company not found", err)
}
ctx.Data = &arn.OpenGraph{
customCtx := ctx.(*middleware.OpenGraphContext)
customCtx.OpenGraph = &arn.OpenGraph{
Tags: map[string]string{
"og:title": company.Name.English,
"og:url": "https://" + ctx.App.Config.Domain + company.Link(),
"og:url": "https://" + assets.Domain + company.Link(),
"og:site_name": "notify.moe",
// "og:image": company.Image,
},

View File

@ -12,7 +12,7 @@ import (
)
// AnimeList ...
func AnimeList(ctx *aero.Context) string {
func AnimeList(ctx aero.Context) error {
user := utils.GetUser(ctx)
nickA := ctx.Get("nick-1")
nickB := ctx.Get("nick-2")

View File

@ -18,7 +18,7 @@ const (
)
// Get edit log.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
index, _ := ctx.GetInt("index")
nick := ctx.Get("nick")

View File

@ -1,6 +1,7 @@
package editor
import (
"net/http"
"strconv"
"github.com/aerogo/aero"
@ -10,11 +11,11 @@ import (
)
// Get ...
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil || (user.Role != "admin" && user.Role != "editor") {
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
ignoreDifferences := arn.FilterIgnoreAnimeDifferences(func(entry *arn.IgnoreAnimeDifference) bool {
@ -43,5 +44,5 @@ func Get(ctx *aero.Context) string {
scoreTitle += objectType + ": " + strconv.Itoa(score) + "\n"
}
return ctx.HTML(components.Editor(ctx.URI(), score, scoreTitle, scoreTypes, user))
return ctx.HTML(components.Editor(ctx.Path(), score, scoreTitle, scoreTypes, user))
}

View File

@ -6,7 +6,7 @@ import (
)
// All ...
func All(ctx *aero.Context) string {
func All(ctx aero.Context) error {
return editorList(
ctx,
"All anime",

View File

@ -6,7 +6,7 @@ import (
)
// AniList ...
func AniList(ctx *aero.Context) string {
func AniList(ctx aero.Context) error {
return editorList(
ctx,
"Anime without Anilist mappings",

View File

@ -6,7 +6,7 @@ import (
)
// Characters ...
func Characters(ctx *aero.Context) string {
func Characters(ctx aero.Context) error {
return editorList(
ctx,
"Anime without characters",

View File

@ -6,7 +6,7 @@ import (
)
// DuplicateMappings ...
func DuplicateMappings(ctx *aero.Context) string {
func DuplicateMappings(ctx aero.Context) error {
return editorList(
ctx,
"Anime with duplicate mappings",

View File

@ -6,7 +6,7 @@ import (
)
// EpisodeLength ...
func EpisodeLength(ctx *aero.Context) string {
func EpisodeLength(ctx aero.Context) error {
return editorList(
ctx,
"Anime without an episode length",

View File

@ -6,7 +6,7 @@ import (
)
// Genres ...
func Genres(ctx *aero.Context) string {
func Genres(ctx aero.Context) error {
return editorList(
ctx,
"Anime without genres",

View File

@ -6,7 +6,7 @@ import (
)
// Licensors ...
func Licensors(ctx *aero.Context) string {
func Licensors(ctx aero.Context) error {
return editorList(
ctx,
"Anime without licensors",

View File

@ -6,16 +6,16 @@ import (
)
// LowResolutionAnimeImages filters anime with low resolution images.
func LowResolutionAnimeImages(ctx *aero.Context) string {
func LowResolutionAnimeImages(ctx aero.Context) error {
return filterAnimeImages(ctx, "Anime with low resolution images", arn.AnimeImageLargeWidth, arn.AnimeImageLargeHeight)
}
// UltraLowResolutionAnimeImages filters anime with ultra low resolution images.
func UltraLowResolutionAnimeImages(ctx *aero.Context) string {
func UltraLowResolutionAnimeImages(ctx aero.Context) error {
return filterAnimeImages(ctx, "Anime with ultra low resolution images", arn.AnimeImageLargeWidth/2, arn.AnimeImageLargeHeight/2)
}
func filterAnimeImages(ctx *aero.Context, title string, minExpectedWidth int, minExpectedHeight int) string {
func filterAnimeImages(ctx aero.Context, title string, minExpectedWidth int, minExpectedHeight int) error {
return editorList(
ctx,
title,

View File

@ -6,7 +6,7 @@ import (
)
// MAL ...
func MAL(ctx *aero.Context) string {
func MAL(ctx aero.Context) error {
return editorList(
ctx,
"Anime without MAL mappings",

View File

@ -6,7 +6,7 @@ import (
)
// Producers ...
func Producers(ctx *aero.Context) string {
func Producers(ctx aero.Context) error {
return editorList(
ctx,
"Anime without producers",

View File

@ -6,7 +6,7 @@ import (
)
// Relations ...
func Relations(ctx *aero.Context) string {
func Relations(ctx aero.Context) error {
return editorList(
ctx,
"Anime without relations",

View File

@ -6,7 +6,7 @@ import (
)
// Shoboi ...
func Shoboi(ctx *aero.Context) string {
func Shoboi(ctx aero.Context) error {
return editorList(
ctx,
"Anime without Shoboi mappings",

View File

@ -6,7 +6,7 @@ import (
)
// Source ...
func Source(ctx *aero.Context) string {
func Source(ctx aero.Context) error {
return editorList(
ctx,
"Anime without a source",

View File

@ -8,7 +8,7 @@ import (
)
// StartDate ...
func StartDate(ctx *aero.Context) string {
func StartDate(ctx aero.Context) error {
return editorList(
ctx,
"Anime without a valid start date",

View File

@ -6,7 +6,7 @@ import (
)
// Studios ...
func Studios(ctx *aero.Context) string {
func Studios(ctx aero.Context) error {
return editorList(
ctx,
"Anime without studios",

View File

@ -6,7 +6,7 @@ import (
)
// Synopsis ...
func Synopsis(ctx *aero.Context) string {
func Synopsis(ctx aero.Context) error {
return editorList(
ctx,
"Anime without a long synopsis",

View File

@ -8,7 +8,7 @@ import (
)
// Trailers ...
func Trailers(ctx *aero.Context) string {
func Trailers(ctx aero.Context) error {
return editorList(
ctx,
"Anime without trailers",

View File

@ -13,7 +13,7 @@ import (
const maxAnimeEntries = 70
// editorList renders the anime list with the given title and filter.
func editorList(ctx *aero.Context, title string, filter func(*arn.Anime) bool, searchLink func(*arn.Anime) string) string {
func editorList(ctx aero.Context, title string, filter func(*arn.Anime) bool, searchLink func(*arn.Anime) string) error {
user := utils.GetUser(ctx)
if user == nil || (user.Role != "admin" && user.Role != "editor") {
@ -23,7 +23,7 @@ func editorList(ctx *aero.Context, title string, filter func(*arn.Anime) bool, s
animes, count := filterAnime(ctx, user, filter)
// Determine URL
url := strings.TrimPrefix(ctx.URI(), "/_")
url := strings.TrimPrefix(ctx.Path(), "/_")
urlParts := strings.Split(url, "/")
urlParts = urlParts[:len(urlParts)-4]
url = strings.Join(urlParts, "/")
@ -40,7 +40,7 @@ func editorList(ctx *aero.Context, title string, filter func(*arn.Anime) bool, s
// filterAnime filters anime by the given filter function and
// additionally applies year and types filters if specified.
func filterAnime(ctx *aero.Context, user *arn.User, filter func(*arn.Anime) bool) ([]*arn.Anime, int) {
func filterAnime(ctx aero.Context, user *arn.User, filter func(*arn.Anime) bool) ([]*arn.Anime, int) {
year := ctx.Get("year")
status := ctx.Get("status")
season := ctx.Get("season")

View File

@ -1,6 +1,8 @@
package filtercompanies
import (
"net/http"
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/components"
@ -10,11 +12,11 @@ import (
const maxEntries = 70
// NoDescription ...
func NoDescription(ctx *aero.Context) string {
func NoDescription(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil || (user.Role != "admin" && user.Role != "editor") {
return ctx.Redirect("/")
return ctx.Redirect(http.StatusFound, "/")
}
companies := arn.FilterCompanies(func(company *arn.Company) bool {
@ -29,5 +31,5 @@ func NoDescription(ctx *aero.Context) string {
companies = companies[:maxEntries]
}
return ctx.HTML(components.CompaniesEditorList(companies, count, ctx.URI(), user))
return ctx.HTML(components.CompaniesEditorList(companies, count, ctx.Path(), user))
}

View File

@ -6,7 +6,7 @@ import (
)
// File shows soundtracks without an audio file.
func File(ctx *aero.Context) string {
func File(ctx aero.Context) error {
return editorList(
ctx,
"Soundtracks without an audio file",

View File

@ -6,7 +6,7 @@ import (
)
// Links shows soundtracks without links.
func Links(ctx *aero.Context) string {
func Links(ctx aero.Context) error {
return editorList(
ctx,
"Soundtracks without links",

View File

@ -8,7 +8,7 @@ import (
)
// MissingLyrics shows soundtracks without lyrics.
func MissingLyrics(ctx *aero.Context) string {
func MissingLyrics(ctx aero.Context) error {
return editorList(
ctx,
"Soundtracks without lyrics",
@ -26,7 +26,7 @@ func MissingLyrics(ctx *aero.Context) string {
}
// UnalignedLyrics shows soundtracks with unaligned lyrics.
func UnalignedLyrics(ctx *aero.Context) string {
func UnalignedLyrics(ctx aero.Context) error {
return editorList(
ctx,
"Soundtracks with unaligned lyrics",

View File

@ -6,7 +6,7 @@ import (
)
// Tags shows soundtracks with less than 3 tags.
func Tags(ctx *aero.Context) string {
func Tags(ctx aero.Context) error {
return editorList(
ctx,
"Soundtracks with less than 3 tags",

View File

@ -13,7 +13,7 @@ import (
const maxSoundTrackEntries = 70
// editorList renders the soundtrack list with the given title and filter.
func editorList(ctx *aero.Context, title string, filter func(*arn.SoundTrack) bool, searchLink func(*arn.SoundTrack) string) string {
func editorList(ctx aero.Context, title string, filter func(*arn.SoundTrack) bool, searchLink func(*arn.SoundTrack) string) error {
user := utils.GetUser(ctx)
if user == nil || (user.Role != "admin" && user.Role != "editor") {
@ -21,7 +21,7 @@ func editorList(ctx *aero.Context, title string, filter func(*arn.SoundTrack) bo
}
tracks, count := filterSoundTracks(ctx, user, filter)
url := strings.TrimPrefix(ctx.URI(), "/_")
url := strings.TrimPrefix(ctx.Path(), "/_")
return ctx.HTML(components.SoundTracksEditorListFull(
title,
@ -34,7 +34,7 @@ func editorList(ctx *aero.Context, title string, filter func(*arn.SoundTrack) bo
}
// filterSoundTracks filters soundtracks by the given filter function.
func filterSoundTracks(ctx *aero.Context, user *arn.User, filter func(*arn.SoundTrack) bool) ([]*arn.SoundTrack, int) {
func filterSoundTracks(ctx aero.Context, user *arn.User, filter func(*arn.SoundTrack) bool) ([]*arn.SoundTrack, int) {
// Filter
tracks := arn.FilterSoundTracks(func(track *arn.SoundTrack) bool {
return !track.IsDraft && filter(track)

View File

@ -29,7 +29,7 @@ var jobInfo = map[string]*utils.JobInfo{
var jobLogs = []string{}
// Overview shows all background jobs.
func Overview(ctx *aero.Context) string {
func Overview(ctx aero.Context) error {
user := utils.GetUser(ctx)
jobs := []*utils.JobInfo{}
@ -41,5 +41,5 @@ func Overview(ctx *aero.Context) string {
return jobs[i].Name < jobs[j].Name
})
return ctx.HTML(components.EditorJobs(jobs, jobLogs, ctx.URI(), user))
return ctx.HTML(components.EditorJobs(jobs, jobLogs, ctx.Path(), user))
}

View File

@ -14,7 +14,7 @@ import (
var jobStartMutex sync.Mutex
// Start will start the specified background job.
func Start(ctx *aero.Context) string {
func Start(ctx aero.Context) error {
jobStartMutex.Lock()
defer jobStartMutex.Unlock()
@ -38,5 +38,5 @@ func Start(ctx *aero.Context) string {
job.Start()
jobLogs = append(jobLogs, user.Nick+" started "+job.Name+" job ("+arn.DateTimeUTC()+").")
return "ok"
return nil
}

View File

@ -11,7 +11,7 @@ import (
)
// NewKitsuAnime ...
func NewKitsuAnime(ctx *aero.Context) string {
func NewKitsuAnime(ctx aero.Context) error {
user := utils.GetUser(ctx)
finder := arn.NewAnimeFinder("kitsu/anime")
deletedIDs, err := arn.GetIDList("deleted kitsu anime")
@ -31,5 +31,5 @@ func NewKitsuAnime(ctx *aero.Context) string {
return a.ID > b.ID
})
return ctx.HTML(components.NewKitsuAnime(animes, ctx.URI(), user))
return ctx.HTML(components.NewKitsuAnime(animes, ctx.Path(), user))
}

View File

@ -18,7 +18,7 @@ const maxCompareMALEntries = 15
type diffFunction func(*arn.Anime, *mal.Anime) []animediff.Difference
// CompareMAL ...
func CompareMAL(ctx *aero.Context) string {
func CompareMAL(ctx aero.Context) error {
user := utils.GetUser(ctx)
year := ctx.Get("year")
status := ctx.Get("status")

View File

@ -9,21 +9,21 @@ import (
)
// Get anime list in the browser extension.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
if user == nil {
return utils.AllowEmbed(ctx, ctx.HTML(components.Login("_blank")))
return ctx.HTML(components.Login("_blank"))
}
if !user.HasBasicInfo() {
return utils.AllowEmbed(ctx, ctx.HTML(components.ExtensionEnterBasicInfo()))
return ctx.HTML(components.ExtensionEnterBasicInfo())
}
// Extension is enabled as long as the site isn't finished yet.
// ---
// if !user.IsPro() && user.TimeSinceRegistered() > 14*24*time.Hour {
// return utils.AllowEmbed(ctx, ctx.HTML(components.EmbedProNotice(user)))
// return ctx.HTML(components.EmbedProNotice(user))
// }
animeList := user.AnimeList()
@ -35,5 +35,5 @@ func Get(ctx *aero.Context) string {
watchingList := animeList.Watching()
watchingList.Sort()
return utils.AllowEmbed(ctx, ctx.HTML(components.BrowserExtension(watchingList, animeList.User(), user)))
return ctx.HTML(components.BrowserExtension(watchingList, animeList.User(), user))
}

View File

@ -34,7 +34,7 @@ func init() {
}
// Get renders the anime episode.
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
id := ctx.Get("id")
episodeNumber, err := ctx.GetInt("episode-number")

View File

@ -10,7 +10,7 @@ import (
)
// Subtitles returns the subtitles.
func Subtitles(ctx *aero.Context) string {
func Subtitles(ctx aero.Context) error {
id := ctx.Get("id")
language := ctx.Get("language")
episodeNumber, err := ctx.GetInt("episode-number")
@ -26,8 +26,8 @@ func Subtitles(ctx *aero.Context) string {
return ctx.Error(http.StatusNotFound, "Anime not found", err)
}
ctx.Response().Header().Set("Access-Control-Allow-Origin", "*")
ctx.Response().Header().Set("Content-Type", "text/vtt; charset=utf-8")
ctx.Response().SetHeader("Access-Control-Allow-Origin", "*")
ctx.Response().SetHeader("Content-Type", "text/vtt; charset=utf-8")
obj, err := spaces.GetObject("arn", fmt.Sprintf("videos/anime/%s/%d.%s.vtt", anime.ID, episodeNumber, language), minio.GetObjectOptions{})

View File

@ -11,7 +11,7 @@ import (
)
// Filter ...
func Filter(ctx *aero.Context) string {
func Filter(ctx aero.Context) error {
year := ctx.Get("year")
season := ctx.Get("season")
status := ctx.Get("status")

View File

@ -18,7 +18,7 @@ const (
)
// AnimeByAverageColor returns all anime with an image in the given color.
func AnimeByAverageColor(ctx *aero.Context) string {
func AnimeByAverageColor(ctx aero.Context) error {
user := utils.GetUser(ctx)
color := ctx.Get("color")
index, _ := ctx.GetInt("index")

View File

@ -12,7 +12,7 @@ import (
)
// Sequels ...
func Sequels(ctx *aero.Context) string {
func Sequels(ctx aero.Context) error {
nick := ctx.Get("nick")
user := utils.GetUser(ctx)
viewUser, err := arn.GetUserByNick(nick)

View File

@ -13,7 +13,7 @@ import (
const minYear = 1963
// Get ...
func Get(ctx *aero.Context) string {
func Get(ctx aero.Context) error {
user := utils.GetUser(ctx)
maxYear := time.Now().Year() - 1
hallOfFameEntries := []*utils.HallOfFameEntry{}

Some files were not shown because too many files have changed in this diff Show More