rc-1
This commit is contained in:
+199
@@ -0,0 +1,199 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gifuu/routes"
|
||||
"gifuu/tools"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Local = time.UTC
|
||||
|
||||
// Startup Services
|
||||
var stopCtx, stop = context.WithCancel(context.Background())
|
||||
var stopWg sync.WaitGroup
|
||||
var syncWg sync.WaitGroup
|
||||
|
||||
tools.LoggerInit.Log(tools.INFO, "Starting Services")
|
||||
for _, fn := range []func(stop context.Context, await *sync.WaitGroup){
|
||||
tools.SetupDatabase,
|
||||
tools.SetupModel,
|
||||
} {
|
||||
syncWg.Add(1)
|
||||
go func() {
|
||||
defer syncWg.Done()
|
||||
fn(stopCtx, &stopWg)
|
||||
}()
|
||||
}
|
||||
syncWg.Wait()
|
||||
go StartupHTTP(stopCtx, &stopWg)
|
||||
|
||||
// Await Shutdown Signal
|
||||
cancel := make(chan os.Signal, 1)
|
||||
signal.Notify(cancel, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-cancel
|
||||
stop()
|
||||
|
||||
// Begin Shutdown Process
|
||||
tools.LoggerInit.Log(tools.WARN, "Shutting Down!")
|
||||
timeout, finish := context.WithTimeout(context.Background(), tools.TIMEOUT_CONTEXT)
|
||||
defer finish()
|
||||
go func() {
|
||||
<-timeout.Done()
|
||||
if timeout.Err() == context.DeadlineExceeded {
|
||||
tools.LoggerInit.Log(tools.FATAL, "Shutdown Deadline Exceeded")
|
||||
return
|
||||
}
|
||||
}()
|
||||
stopWg.Wait()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func SetupMux() http.HandlerFunc {
|
||||
|
||||
var (
|
||||
mux = http.NewServeMux()
|
||||
limitPublic = tools.NewRatelimiter("PUBLIC", 900, 5*time.Minute)
|
||||
limitDelete = tools.NewRatelimiter("DELETE", 100, 5*time.Minute)
|
||||
limitStart = tools.NewRatelimiter("START", 100, 5*time.Minute)
|
||||
limitCreate = tools.NewRatelimiter("CREATE", 50, 5*time.Minute)
|
||||
limitModerators = tools.NewRatelimiter("MOD", 300, 5*time.Minute)
|
||||
gatekeepModerator = tools.NewGatekeeper(true)
|
||||
powExpensive = tools.NewChallenger(20)
|
||||
powNormal = tools.NewChallenger(18)
|
||||
)
|
||||
|
||||
// General
|
||||
mux.Handle("/uploads", tools.MethodHandler{
|
||||
http.MethodPost: tools.Chain(routes.POST_Uploads, limitCreate, powExpensive),
|
||||
})
|
||||
mux.Handle("/tags/popular", tools.MethodHandler{
|
||||
http.MethodGet: tools.Chain(routes.GET_Tags_Popular, limitPublic),
|
||||
})
|
||||
mux.Handle("/tags/autocomplete", tools.MethodHandler{
|
||||
http.MethodGet: tools.Chain(routes.GET_Tags_Autocomplete, limitPublic),
|
||||
})
|
||||
mux.Handle("/art/latest", tools.MethodHandler{
|
||||
http.MethodGet: tools.Chain(routes.GET_Art_Latest, limitPublic),
|
||||
})
|
||||
mux.Handle("/art/search", tools.MethodHandler{
|
||||
http.MethodGet: tools.Chain(routes.GET_Art_Search, limitPublic),
|
||||
})
|
||||
mux.Handle("/art/{id}", tools.MethodHandler{
|
||||
http.MethodGet: tools.Chain(routes.GET_Art_ID, limitPublic),
|
||||
http.MethodDelete: tools.Chain(routes.DELETE_Art_ID, limitDelete),
|
||||
})
|
||||
mux.Handle("/art/{id}/report", tools.MethodHandler{
|
||||
http.MethodPost: tools.Chain(routes.POST_Art_ID_Reports, limitCreate, powNormal),
|
||||
})
|
||||
mux.Handle("/metadata/{id}", tools.MethodHandler{
|
||||
http.MethodGet: tools.Chain(routes.GET_Metadata_ID, limitPublic),
|
||||
})
|
||||
|
||||
// Moderation
|
||||
mux.Handle("/moderation/art/{id}", tools.MethodHandler{
|
||||
http.MethodDelete: tools.Chain(routes.DELETE_Moderation_Art_ID, limitModerators, gatekeepModerator),
|
||||
})
|
||||
// mux.Handle("/moderation/art/{id}/rating", tools.MethodHandler{
|
||||
// // TODO: Sets the items moderation to 100% hiding it from the public
|
||||
// http.MethodPatch: tools.Chain(routes.PATCH_Moderation_Art_ID_Rating, limitModerators, gatekeepModerator),
|
||||
// })
|
||||
// mux.Handle("/moderation/art/{id}/bypass", tools.MethodHandler{
|
||||
// // TODO: Disables Reporting for the item by setting the 'flag_bypass' flag
|
||||
// http.MethodPatch: tools.Chain(routes.PATCH_Moderation_Art_ID_Bypass, limitModerators, gatekeepModerator),
|
||||
// })
|
||||
// mux.Handle("/moderation/art/{id}/reports", tools.MethodHandler{
|
||||
// // TODO: Get Latest Reports for an Item
|
||||
// http.MethodGet: tools.Chain(routes.GET_Moderation_Art_ID_Reports, limitModerators, gatekeepModerator),
|
||||
// // TODO: Delete all Reports for an Item
|
||||
// http.MethodDelete: tools.Chain(routes.DELETE_Moderation_Art_ID_Reports, limitModerators, gatekeepModerator),
|
||||
// })
|
||||
// // TODO: Get the Latest Reports across Site
|
||||
// mux.Handle("/moderation/latest", tools.MethodHandler{
|
||||
// http.MethodGet: tools.Chain(routes.GET_Moderation_Latest, limitModerators, gatekeepModerator),
|
||||
// })
|
||||
|
||||
// Other
|
||||
mux.Handle("/challenge", tools.MethodHandler{
|
||||
http.MethodGet: tools.Chain(routes.GET_Challenge, limitStart),
|
||||
})
|
||||
mux.Handle("/limits", tools.MethodHandler{
|
||||
http.MethodGet: tools.Chain(routes.GET_Limits, limitPublic),
|
||||
})
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
tools.SendClientError(w, r, tools.ERROR_UNKNOWN_ENDPOINT)
|
||||
})
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Inject CORS
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
if r.Method == http.MethodOptions {
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Challenge-Nonce, X-Challenge-Counter")
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
mux.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func StartupHTTP(stop context.Context, await *sync.WaitGroup) {
|
||||
var listener net.Listener
|
||||
var err error
|
||||
|
||||
if strings.HasPrefix(tools.HTTP_ADDRESS, "unix/") {
|
||||
path := strings.TrimPrefix(tools.HTTP_ADDRESS, "unix/")
|
||||
os.Remove(path)
|
||||
listener, err = net.Listen("unix", path)
|
||||
if err == nil {
|
||||
os.Chmod(path, 0660)
|
||||
}
|
||||
} else {
|
||||
listener, err = net.Listen("tcp", tools.HTTP_ADDRESS)
|
||||
}
|
||||
if err != nil {
|
||||
tools.LoggerHTTP.Log(tools.FATAL, "Listen failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
svr := http.Server{
|
||||
Handler: SetupMux(),
|
||||
MaxHeaderBytes: 4096,
|
||||
IdleTimeout: 10 * time.Second,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
// Shutdown Logic
|
||||
await.Add(1)
|
||||
go func() {
|
||||
defer await.Done()
|
||||
<-stop.Done()
|
||||
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), tools.TIMEOUT_SHUTDOWN)
|
||||
defer cancel()
|
||||
|
||||
if err := svr.Shutdown(shutdownCtx); err != nil {
|
||||
tools.LoggerHTTP.Log(tools.ERROR, "Shutdown error: %s", err)
|
||||
}
|
||||
|
||||
tools.LoggerHTTP.Log(tools.INFO, "Closed")
|
||||
}()
|
||||
|
||||
tools.LoggerHTTP.Log(tools.INFO, "Listening @ %s", tools.HTTP_ADDRESS)
|
||||
if err := svr.Serve(listener); err != http.ErrServerClosed {
|
||||
tools.LoggerHTTP.Log(tools.FATAL, "Startup Failed: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user