diff --git a/main.go b/main.go index 6977d090..7c45b359 100644 --- a/main.go +++ b/main.go @@ -1,110 +1,9 @@ package main import ( - "net/http" - "strings" - - "github.com/aerogo/aero" - nanostore "github.com/aerogo/session-store-nano" - "github.com/akyoto/color" - "github.com/animenotifier/notify.moe/arn" - "github.com/animenotifier/notify.moe/assets" - "github.com/animenotifier/notify.moe/auth" - "github.com/animenotifier/notify.moe/graphql" - "github.com/animenotifier/notify.moe/middleware" - "github.com/animenotifier/notify.moe/pages" - "github.com/animenotifier/notify.moe/utils/htmlemail" - "github.com/animenotifier/notify.moe/utils/https" - "github.com/animenotifier/notify.moe/utils/routetests" + "github.com/animenotifier/notify.moe/server" ) func main() { - // Configure and start - app := aero.New() - configure(app).Run() -} - -func configure(app *aero.Application) *aero.Application { - // Sessions - app.Sessions.Duration = 3600 * 24 * 30 * 6 - app.Sessions.Store = nanostore.New(arn.DB.Collection("Session")) - app.Sessions.SameSite = http.SameSiteNoneMode - - // Content security policy - app.ContentSecurityPolicy.Set("img-src", "https: data:") - app.ContentSecurityPolicy.Set("connect-src", "https: wss: data:") - app.ContentSecurityPolicy.Set("font-src", "https: data:") - - // Security - https.Configure(app) - - // Assets - assets.Configure(app) - - // Pages - pages.Configure(app) - - // Rewrite - app.Rewrite(pages.Rewrite) - - // Middleware - app.Use( - middleware.Recover, - middleware.HTTPSRedirect, - middleware.OpenGraph, - middleware.Log, - middleware.Session, - middleware.UserInfo, - ) - - // API - arn.API.Install(app) - - // Development server configuration - if arn.IsDevelopment() { - assets.Domain = "beta.notify.moe" - assets.Manifest.Name += " - Beta" - } - - // Authentication - auth.Install(app) - - // GraphQL - graphql.Install(app) - - // Close the database node on shutdown - app.OnEnd(arn.Node.Close) - - // Don't push when an underscore URL has been requested - app.AddPushCondition(func(ctx aero.Context) bool { - return !strings.HasPrefix(ctx.Path(), "/_") - }) - - // Show errors in the console - app.OnError(func(ctx aero.Context, err error) { - color.Red(err.Error()) - }) - - // Emails - arn.HTMLEmailRenderer = &htmlemail.Renderer{} - - // Check that this is the server - if !arn.Node.IsServer() && !arn.IsTest() { - panic("Another program is currently running as the database server") - } - - // Prefetch all collections - 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("Referer"), "/service-worker") - }) - - // Specify test routes - for route, examples := range routetests.All() { - app.Test(route, examples...) - } - - return app + server.Main() } diff --git a/main_test.go b/main_test.go index 390252f1..5fdcc2c1 100644 --- a/main_test.go +++ b/main_test.go @@ -6,111 +6,103 @@ import ( "strings" "testing" - "github.com/aerogo/aero" "github.com/akyoto/assert" "github.com/animenotifier/notify.moe/arn" + "github.com/animenotifier/notify.moe/server" "github.com/animenotifier/notify.moe/utils/routetests" ) func TestRoutes(t *testing.T) { - t.Parallel() - app := configure(aero.New()) + app := server.New() app.BindMiddleware() // Iterate through every route for _, examples := range routetests.All() { // Iterate through every example specified for that route for _, example := range examples { - testRoute(t, app, example) + fetch(t, app, example) } } } -func TestAnimePages(t *testing.T) { - t.Parallel() - app := configure(aero.New()) +func TestAnime(t *testing.T) { + app := server.New() app.BindMiddleware() for anime := range arn.StreamAnime() { - testRoute(t, app, anime.Link()) + fetch(t, app, anime.Link()) } } -func TestSoundTrackPages(t *testing.T) { - t.Parallel() - app := configure(aero.New()) +func TestSoundTracks(t *testing.T) { + app := server.New() app.BindMiddleware() for soundtrack := range arn.StreamSoundTracks() { - testRoute(t, app, soundtrack.Link()) + fetch(t, app, soundtrack.Link()) assert.NotNil(t, soundtrack.Creator()) } } -func TestAMVPages(t *testing.T) { - t.Parallel() - app := configure(aero.New()) +func TestAMVs(t *testing.T) { + app := server.New() app.BindMiddleware() for amv := range arn.StreamAMVs() { - testRoute(t, app, amv.Link()) + fetch(t, app, amv.Link()) assert.NotNil(t, amv.Creator()) } } -func TestCompanyPages(t *testing.T) { - t.Parallel() - app := configure(aero.New()) +func TestCompanies(t *testing.T) { + app := server.New() app.BindMiddleware() for company := range arn.StreamCompanies() { - testRoute(t, app, company.Link()) + fetch(t, app, company.Link()) } } -func TestThreadPages(t *testing.T) { - t.Parallel() - app := configure(aero.New()) +func TestThreads(t *testing.T) { + app := server.New() app.BindMiddleware() for thread := range arn.StreamThreads() { - testRoute(t, app, thread.Link()) + fetch(t, app, thread.Link()) assert.NotNil(t, thread.Creator()) } } -func TestPostPages(t *testing.T) { - t.Parallel() - app := configure(aero.New()) +func TestPosts(t *testing.T) { + app := server.New() app.BindMiddleware() for post := range arn.StreamPosts() { - testRoute(t, app, post.Link()) + fetch(t, app, post.Link()) assert.NotNil(t, post.Creator()) } } -func TestQuotePages(t *testing.T) { - t.Parallel() - app := configure(aero.New()) +func TestQuotes(t *testing.T) { + app := server.New() app.BindMiddleware() for quote := range arn.StreamQuotes() { - testRoute(t, app, quote.Link()) + fetch(t, app, quote.Link()) assert.NotNil(t, quote.Creator()) } } -// func TestUserPages(t *testing.T) { -// t.Parallel() -// app := configure(aero.New()) +func TestUsers(t *testing.T) { + app := server.New() + app.BindMiddleware() -// for user := range arn.StreamUsers() { -// testRoute(t, app, user.Link()) -// } -// } + for user := range arn.StreamUsers() { + fetch(t, app, user.Link()) + } +} -func testRoute(t *testing.T, app http.Handler, route string) { +func fetch(t *testing.T, app http.Handler, route string) { request := httptest.NewRequest("GET", strings.ReplaceAll(route, " ", "%20"), nil) response := httptest.NewRecorder() app.ServeHTTP(response, request) diff --git a/server/Main.go b/server/Main.go new file mode 100644 index 00000000..86f6b2b0 --- /dev/null +++ b/server/Main.go @@ -0,0 +1,6 @@ +package server + +func Main() { + app := New() + app.Run() +} diff --git a/server/New.go b/server/New.go new file mode 100644 index 00000000..664b2a79 --- /dev/null +++ b/server/New.go @@ -0,0 +1,106 @@ +package server + +import ( + "net/http" + "strings" + + "github.com/aerogo/aero" + nanostore "github.com/aerogo/session-store-nano" + "github.com/akyoto/color" + "github.com/animenotifier/notify.moe/arn" + "github.com/animenotifier/notify.moe/assets" + "github.com/animenotifier/notify.moe/auth" + "github.com/animenotifier/notify.moe/graphql" + "github.com/animenotifier/notify.moe/middleware" + "github.com/animenotifier/notify.moe/pages" + "github.com/animenotifier/notify.moe/utils/htmlemail" + "github.com/animenotifier/notify.moe/utils/https" + "github.com/animenotifier/notify.moe/utils/routetests" +) + +func New() *aero.Application { + app := aero.New() + + // Sessions + app.Sessions.Duration = 3600 * 24 * 30 * 6 + app.Sessions.Store = nanostore.New(arn.DB.Collection("Session")) + app.Sessions.SameSite = http.SameSiteNoneMode + + // Content security policy + app.ContentSecurityPolicy.Set("img-src", "https: data:") + app.ContentSecurityPolicy.Set("connect-src", "https: wss: data:") + app.ContentSecurityPolicy.Set("font-src", "https: data:") + + // Security + https.Configure(app) + + // Assets + assets.Configure(app) + + // Pages + pages.Configure(app) + + // Rewrite + app.Rewrite(pages.Rewrite) + + // Middleware + app.Use( + middleware.Recover, + middleware.HTTPSRedirect, + middleware.OpenGraph, + middleware.Log, + middleware.Session, + middleware.UserInfo, + ) + + // API + arn.API.Install(app) + + // Development server configuration + if arn.IsDevelopment() { + assets.Domain = "beta.notify.moe" + assets.Manifest.Name += " - Beta" + } + + // Authentication + auth.Install(app) + + // GraphQL + graphql.Install(app) + + // Close the database node on shutdown + app.OnEnd(arn.Node.Close) + + // Don't push when an underscore URL has been requested + app.AddPushCondition(func(ctx aero.Context) bool { + return !strings.HasPrefix(ctx.Path(), "/_") + }) + + // Show errors in the console + app.OnError(func(ctx aero.Context, err error) { + color.Red(err.Error()) + }) + + // Emails + arn.HTMLEmailRenderer = &htmlemail.Renderer{} + + // Check that this is the server + if !arn.Node.IsServer() && !arn.IsTest() { + panic("Another program is currently running as the database server") + } + + // Prefetch all collections + 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("Referer"), "/service-worker") + }) + + // Specify test routes + for route, examples := range routetests.All() { + app.Test(route, examples...) + } + + return app +}