keyproofs/main.go

214 lines
4.9 KiB
Go

package main
import (
"context"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
lru "github.com/hashicorp/golang-lru"
_ "github.com/joho/godotenv/autoload"
"github.com/rs/cors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"gosrc.io/xmpp"
"github.com/sour-is/keyproofs/pkg/cache"
"github.com/sour-is/keyproofs/pkg/config"
"github.com/sour-is/keyproofs/pkg/graceful"
"github.com/sour-is/keyproofs/pkg/httpsrv"
app_avatar "github.com/sour-is/keyproofs/pkg/app/avatar"
app_dns "github.com/sour-is/keyproofs/pkg/app/dns"
app_keyproofs "github.com/sour-is/keyproofs/pkg/app/keyproofs"
app_vcard "github.com/sour-is/keyproofs/pkg/app/vcard"
app_wkd "github.com/sour-is/keyproofs/pkg/app/wkd"
)
var (
// AppName Application Name
AppName string = "KeyProofs"
// AppVersion Application Version Number
AppVersion string
// AppBuild Application Build Hash
BuildHash string
// AppDate Application Build Date
BuildDate string
)
func main() {
log := zerolog.New(zerolog.NewConsoleWriter()).
With().
Timestamp().
Caller().
Logger()
ctx := context.Background()
ctx = log.WithContext(ctx)
ctx = graceful.WithInterupt(ctx)
ctx, _ = graceful.WithWaitGroup(ctx)
cfg := config.New()
cfg.Set("app-name", AppName)
cfg.Set("app-version", AppVersion)
cfg.Set("build-hash", BuildHash)
cfg.Set("build-date", BuildDate)
ctx = cfg.Apply(ctx)
log.Info().
Str("app", AppName).
Str("version", AppVersion).
Str("build-hash", BuildHash).
Str("build-date", BuildDate).
Msg("startup...")
if err := run(ctx); err != nil {
log.Error().Err(err).Msg("Application Failed")
os.Exit(1)
}
}
func run(ctx context.Context) error {
log := log.Ctx(ctx)
wg := graceful.WaitGroup(ctx)
cfg := config.FromContext(ctx)
// derive baseURL from listener options
listen := env("HTTP_LISTEN", ":9061")
host, _ := os.Hostname()
if strings.HasPrefix(listen, ":") {
host += listen
}
baseURL := fmt.Sprintf("http://%s", host)
// Setup router
cors := cors.New(cors.Options{
AllowCredentials: true,
AllowedMethods: strings.Fields(env("CORS_METHODS", "GET")),
AllowedOrigins: strings.Fields(env("CORS_ORIGIN", "*")),
})
logFmt := &middleware.DefaultLogFormatter{Logger: accessLog(log.Info)}
mux := chi.NewRouter()
mux.Use(
middleware.RequestID,
middleware.RealIP,
middleware.Recoverer,
middleware.RequestLogger(logFmt),
secHeaders,
cors.Handler,
addLogger(log),
cfg.ApplyHTTP,
)
if env("DISABLE_KEYPROOF", "false") == "false" {
// Set config values
cfg.Set("base-url", env("BASE_URL", baseURL))
cfg.Set("dns-url", env("DNS_URL", baseURL))
cfg.Set("xmpp-url", env("XMPP_URL", baseURL))
cfg.Set("reddit.api-key", os.Getenv("REDDIT_APIKEY"))
cfg.Set("reddit.secret", os.Getenv("REDDIT_SECRET"))
cfg.Set("github.secret", os.Getenv("GITHUB_SECRET"))
// Create cache for promise engine
arc, _ := lru.NewARC(4096)
c := cache.New(arc)
app_keyproofs.NewKeyProofApp(ctx, c).Routes(mux)
}
if env("DISABLE_DNS", "false") == "false" {
app_dns.New(ctx).Routes(mux)
}
if env("DISABLE_AVATAR", "false") == "false" {
app, err := app_avatar.New(ctx, env("AVATAR_PATH", "pub"))
if err != nil {
return err
}
app.Routes(mux)
}
if env("DISABLE_WKD", "false") == "false" {
app, err := app_wkd.New(ctx, env("WKD_PATH", "pub"), env("WKD_DOMAIN", "sour.is"))
if err != nil {
return err
}
app.Routes(mux)
}
if env("DISABLE_VCARD", "false") == "false" {
app, err := app_vcard.New(ctx, &xmpp.Config{
Jid: os.Getenv("XMPP_USERNAME"),
Credential: xmpp.Password(os.Getenv("XMPP_PASSWORD")),
})
if err != nil {
return err
}
app.Routes(mux)
}
log.Info().
Str("listen", listen).
Int("user", os.Geteuid()).
Int("group", os.Getgid()).
Msg("running")
err := httpsrv.New(&http.Server{
Addr: listen,
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
Handler: mux,
}).Run(ctx)
if err != nil {
return err
}
return wg.Wait(5 * time.Second)
}
func env(name, defaultValue string) string {
if value := os.Getenv(name); value != "" {
return value
}
return defaultValue
}
func secHeaders(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Security-Policy", "font-src https://pagecdn.io")
h.ServeHTTP(w, r)
})
}
type accessLog func() *zerolog.Event
func (a accessLog) Print(v ...interface{}) {
a().Msg(fmt.Sprint(v...))
}
func addLogger(log *zerolog.Logger) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r = r.WithContext(log.WithContext(r.Context()))
next.ServeHTTP(w, r)
})
}
}