155 lines
3.6 KiB
Go
155 lines
3.6 KiB
Go
// TrevStack HTTP Server
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"connectrpc.com/validate"
|
|
"github.com/go-webauthn/webauthn/webauthn"
|
|
"github.com/joho/godotenv"
|
|
"golang.org/x/net/http2"
|
|
"golang.org/x/net/http2/h2c"
|
|
|
|
embed "github.com/spotdemo4/trevstack/server"
|
|
"github.com/spotdemo4/trevstack/server/internal/database"
|
|
"github.com/spotdemo4/trevstack/server/internal/handlers/client"
|
|
"github.com/spotdemo4/trevstack/server/internal/handlers/file"
|
|
"github.com/spotdemo4/trevstack/server/internal/handlers/item/v1"
|
|
"github.com/spotdemo4/trevstack/server/internal/handlers/user/v1"
|
|
"github.com/spotdemo4/trevstack/server/internal/interceptors"
|
|
)
|
|
|
|
func main() {
|
|
name := "TrevStack"
|
|
|
|
// Get env
|
|
env, err := getEnv()
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
// Migrate database
|
|
err = database.Migrate(env.DatabaseURL, embed.DBFS)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
// Get database
|
|
sqlc, db, err := database.New(env.DatabaseURL)
|
|
if err != nil {
|
|
log.Fatalf("failed to connect to database: %s", err.Error())
|
|
}
|
|
|
|
// Create webauthn
|
|
webAuthn, err := webauthn.New(&webauthn.Config{
|
|
RPDisplayName: name,
|
|
RPID: env.URL.Hostname(),
|
|
RPOrigins: []string{env.URL.String()},
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("failed to create webauthn: %s", err.Error())
|
|
}
|
|
|
|
// Create validate interceptor
|
|
vi, err := validate.NewInterceptor()
|
|
if err != nil {
|
|
log.Fatalf("failed to create validator: %s", err.Error())
|
|
}
|
|
|
|
// Serve gRPC Handlers
|
|
api := http.NewServeMux()
|
|
api.Handle(interceptors.WithCORS(user.NewAuthHandler(vi, sqlc, webAuthn, name, env.Key)))
|
|
api.Handle(interceptors.WithCORS(user.NewHandler(vi, sqlc, webAuthn, name, env.Key)))
|
|
api.Handle(interceptors.WithCORS(item.NewHandler(vi, sqlc, env.Key)))
|
|
|
|
// Serve web interface
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/", client.NewClientHandler(env.Key, embed.ClientFS))
|
|
mux.Handle("/file/", file.NewFileHandler(sqlc, env.Key))
|
|
mux.Handle("/grpc/", http.StripPrefix("/grpc", api))
|
|
|
|
// Start server
|
|
log.Printf("Starting server on :%s", env.Port)
|
|
server := &http.Server{
|
|
Addr: fmt.Sprintf(":%s", env.Port),
|
|
Handler: h2c.NewHandler(mux, &http2.Server{}),
|
|
}
|
|
|
|
// Gracefully shutdown on SIGINT or SIGTERM
|
|
sigs := make(chan os.Signal, 1)
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
go func() {
|
|
sig := <-sigs
|
|
log.Printf("Received signal %s, exiting", sig)
|
|
|
|
// Close HTTP server
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
if err := server.Shutdown(ctx); err != nil {
|
|
server.Close()
|
|
}
|
|
cancel()
|
|
|
|
// Close database connection
|
|
db.Close()
|
|
}()
|
|
|
|
if err := server.ListenAndServe(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
type env struct {
|
|
Port string
|
|
Key string
|
|
URL *url.URL
|
|
DatabaseURL string
|
|
}
|
|
|
|
func getEnv() (*env, error) {
|
|
err := godotenv.Load()
|
|
if err != nil {
|
|
log.Println("Failed to load .env file, using environment variables")
|
|
}
|
|
|
|
// Create
|
|
env := env{
|
|
Port: os.Getenv("PORT"),
|
|
Key: os.Getenv("KEY"),
|
|
DatabaseURL: os.Getenv("DATABASE_URL"),
|
|
}
|
|
|
|
// Validate
|
|
if env.Port == "" {
|
|
env.Port = "8080"
|
|
log.Printf("env 'PORT' not found, defaulting to %s\n", env.Port)
|
|
}
|
|
if env.Key == "" {
|
|
return nil, errors.New("env 'KEY' not found")
|
|
}
|
|
if env.DatabaseURL == "" {
|
|
return nil, errors.New("env 'DATABASE_URL' not found")
|
|
}
|
|
|
|
// Parse URL
|
|
if os.Getenv("URL") == "" {
|
|
env.URL, _ = url.Parse("http://localhost:" + env.Port)
|
|
log.Printf("env 'URL' not found, defaulting to %s\n", env.URL.String())
|
|
} else {
|
|
env.URL, err = url.Parse(os.Getenv("URL"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &env, nil
|
|
}
|