github.com/mistwind/reviewdog@v0.0.0-20230322024206-9cfa11856d58/doghouse/appengine/main.go (about)

     1  package main
     2  
     3  import (
     4  	"log"
     5  	"net/http"
     6  	"os"
     7  	"strconv"
     8  
     9  	"contrib.go.opencensus.io/exporter/stackdriver"
    10  	"contrib.go.opencensus.io/exporter/stackdriver/propagation"
    11  	"github.com/haya14busa/secretbox"
    12  	"github.com/justinas/nosurf"
    13  	"go.opencensus.io/plugin/ochttp"
    14  	"go.opencensus.io/trace"
    15  
    16  	"github.com/mistwind/reviewdog/doghouse/server/cookieman"
    17  	"github.com/mistwind/reviewdog/doghouse/server/storage"
    18  )
    19  
    20  func mustCookieMan() *cookieman.CookieMan {
    21  	// Create secret key by following command.
    22  	// $ ruby -rsecurerandom -e 'puts SecureRandom.hex(32)'
    23  	cipher, err := secretbox.NewFromHexKey(mustGetenv("SECRETBOX_SECRET"))
    24  	if err != nil {
    25  		log.Fatalf("failed to create secretbox: %v", err)
    26  	}
    27  	c := cookieman.CookieOption{
    28  		Cookie: http.Cookie{
    29  			HttpOnly: true,
    30  			Secure:   true,
    31  			Path:     "/",
    32  		},
    33  	}
    34  	return cookieman.New(cipher, c)
    35  }
    36  
    37  func mustGitHubAppsPrivateKey() []byte {
    38  	// Private keys https://github.com/settings/apps/reviewdog
    39  	githubAppsPrivateKey, err := os.ReadFile(mustGetenv("GITHUB_PRIVATE_KEY_FILE"))
    40  	if err != nil {
    41  		log.Fatalf("could not read private key: %s", err)
    42  	}
    43  	return githubAppsPrivateKey
    44  }
    45  
    46  func mustGetenv(name string) string {
    47  	s := os.Getenv(name)
    48  	if s == "" {
    49  		log.Fatalf("%s is not set", name)
    50  	}
    51  	return s
    52  }
    53  
    54  func mustIntEnv(name string) int {
    55  	s := os.Getenv(name)
    56  	if s == "" {
    57  		log.Fatalf("%s is not set", name)
    58  	}
    59  	i, err := strconv.Atoi(s)
    60  	if err != nil {
    61  		log.Fatal(err)
    62  	}
    63  	return i
    64  }
    65  
    66  func main() {
    67  	configureTrace()
    68  	initTemplates()
    69  
    70  	integrationID := mustIntEnv("GITHUB_INTEGRATION_ID")
    71  	ghPrivateKey := mustGitHubAppsPrivateKey()
    72  
    73  	ghInstStore := storage.GitHubInstallationDatastore{}
    74  	ghRepoTokenStore := storage.GitHubRepoTokenDatastore{}
    75  
    76  	ghHandler := NewGitHubHandler(
    77  		mustGetenv("GITHUB_CLIENT_ID"),
    78  		mustGetenv("GITHUB_CLIENT_SECRET"),
    79  		mustCookieMan(),
    80  		ghPrivateKey,
    81  		integrationID,
    82  	)
    83  
    84  	ghChecker := githubChecker{
    85  		privateKey:       ghPrivateKey,
    86  		integrationID:    integrationID,
    87  		ghInstStore:      &ghInstStore,
    88  		ghRepoTokenStore: &ghRepoTokenStore,
    89  		tr: &ochttp.Transport{
    90  			// Use Google Cloud propagation format.
    91  			Propagation: &propagation.HTTPFormat{},
    92  		},
    93  	}
    94  
    95  	ghWebhookHandler := githubWebhookHandler{
    96  		secret:      []byte(mustGetenv("GITHUB_WEBHOOK_SECRET")),
    97  		ghInstStore: &ghInstStore,
    98  	}
    99  
   100  	mu := http.NewServeMux()
   101  
   102  	// Register Admin handlers.
   103  	mu.HandleFunc("/_ah/warmup", warmupHandler)
   104  
   105  	handleFunc(mu, "/", handleTop)
   106  	handleFunc(mu, "/check", ghChecker.handleCheck)
   107  	handleFunc(mu, "/gh_/webhook", ghWebhookHandler.handleWebhook)
   108  	handleFunc(mu, "/gh_/auth/callback", ghHandler.HandleAuthCallback)
   109  	handleFunc(mu, "/gh_/logout", ghHandler.HandleLogout)
   110  	mu.Handle("/gh/", nosurf.New(ochttp.WithRouteTag(ghHandler.LogInHandler(http.HandlerFunc(ghHandler.HandleGitHubTop)), "/gh/")))
   111  
   112  	http.Handle("/", mu)
   113  	log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), &ochttp.Handler{
   114  		Handler:     mu,
   115  		Propagation: &propagation.HTTPFormat{},
   116  	}))
   117  }
   118  
   119  func handleFunc(mu *http.ServeMux, pattern string, handler func(http.ResponseWriter, *http.Request)) {
   120  	mu.Handle(pattern,
   121  		ochttp.WithRouteTag(http.HandlerFunc(handler), pattern))
   122  }
   123  
   124  func handleTop(w http.ResponseWriter, _ *http.Request) {
   125  	var data struct {
   126  		Title string
   127  	}
   128  	data.Title = "reviewdog"
   129  	topTmpl.ExecuteTemplate(w, "base", &data)
   130  }
   131  
   132  // Document: https://cloud.google.com/trace/docs/setup/go
   133  func configureTrace() {
   134  	// Create and register a OpenCensus Stackdriver Trace exporter.
   135  	exporter, err := stackdriver.NewExporter(stackdriver.Options{
   136  		ProjectID: os.Getenv("GOOGLE_CLOUD_PROJECT"),
   137  	})
   138  	if err != nil {
   139  		log.Fatal(err)
   140  	}
   141  	trace.RegisterExporter(exporter)
   142  	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
   143  }