| // scrapexchange is the web admin and API server for the scrap exchange service. |
| // |
| // See http://go/scrap-exchange for more details. |
| package main |
| |
| import ( |
| "context" |
| "flag" |
| "html/template" |
| "net/http" |
| "path/filepath" |
| |
| "cloud.google.com/go/storage" |
| "github.com/gorilla/mux" |
| "github.com/unrolled/secure" |
| "go.skia.org/infra/go/allowed" |
| "go.skia.org/infra/go/auth" |
| "go.skia.org/infra/go/baseapp" |
| "go.skia.org/infra/go/gcs/gcsclient" |
| "go.skia.org/infra/go/httputils" |
| "go.skia.org/infra/go/login" |
| "go.skia.org/infra/go/skerr" |
| "go.skia.org/infra/go/sklog" |
| "go.skia.org/infra/scrap/go/api" |
| "go.skia.org/infra/scrap/go/scrap" |
| "google.golang.org/api/option" |
| ) |
| |
| // flags |
| var ( |
| authGroup = flag.String("auth_group", "google/skia-staff@google.com", "The chrome infra auth group to use for restricting access.") |
| chromeInfraAuthJWT = flag.String("chrome_infra_auth_jwt", "/var/secrets/skia-public-auth/key.json", "The JWT key for the service account that has access to chrome infra auth.") |
| internalPort = flag.String("internal_port", ":9000", "HTTP internal service address (e.g., ':9000') for unauthenticated in-cluster requests.") |
| bucket = flag.String("bucket", "", "The Google Cloud Storage bucket that scraps are stored in.") |
| ) |
| |
| // server is the state of the server. |
| type server struct { |
| apiEndpoints *api.Api |
| templates *template.Template |
| } |
| |
| // New implements baseapp.Constructor. |
| func New() (baseapp.App, error) { |
| if *bucket == "" { |
| return nil, skerr.Fmt("--bucket is a required flag.") |
| } |
| var admin allowed.Allow |
| if !*baseapp.Local { |
| ts, err := auth.NewJWTServiceAccountTokenSource("", *chromeInfraAuthJWT, auth.SCOPE_USERINFO_EMAIL) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| client := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client() |
| admin, err = allowed.NewAllowedFromChromeInfraAuth(client, *authGroup) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| } else { |
| admin = allowed.NewAllowedFromList([]string{"fred@example.org", "barney@example.org", "wilma@example.org"}) |
| } |
| |
| login.SimpleInitWithAllow(*baseapp.Port, *baseapp.Local, admin, nil, nil) |
| |
| ctx := context.Background() |
| ts, err := auth.NewDefaultTokenSource(*baseapp.Local, storage.ScopeFullControl) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| client := httputils.DefaultClientConfig().WithTokenSource(ts).WithoutRetries().Client() |
| storageClient, err := storage.NewClient(ctx, option.WithHTTPClient(client)) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| gcsClient := gcsclient.New(storageClient, *bucket) |
| scrapExchange, err := scrap.New(gcsClient) |
| if err != nil { |
| return nil, skerr.Wrapf(err, "Failed to create ScrapExchange.") |
| } |
| |
| apiEndpoints := api.New(scrapExchange) |
| |
| srv := &server{ |
| apiEndpoints: apiEndpoints, |
| } |
| srv.loadTemplates() |
| |
| srv.startInternalServer() |
| |
| return srv, nil |
| } |
| |
| func (srv *server) loadTemplates() { |
| srv.templates = template.Must(template.New("").Delims("{%", "%}").ParseFiles( |
| filepath.Join(*baseapp.ResourcesDir, "index.html"), |
| )) |
| } |
| |
| func (srv *server) mainHandler(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-Type", "text/html") |
| if *baseapp.Local { |
| srv.loadTemplates() |
| } |
| if err := srv.templates.ExecuteTemplate(w, "index.html", map[string]string{ |
| // Look in webpack.config.js for where the nonce templates are injected. |
| "Nonce": secure.CSPNonce(r.Context()), |
| }); err != nil { |
| sklog.Errorf("Failed to expand template: %s", err) |
| } |
| } |
| |
| // user returns the currently logged in user, or a placeholder if running locally. |
| func (srv *server) user(r *http.Request) string { |
| user := "barney@example.org" |
| if !*baseapp.Local { |
| user = login.LoggedInAs(r) |
| } |
| return user |
| } |
| |
| // AddHandlers implements baseapp.App. |
| func (srv *server) AddHandlers(r *mux.Router) { |
| r.HandleFunc("/", login.RestrictAdminFn(srv.mainHandler)) |
| r.HandleFunc("/loginstatus/", login.StatusHandler).Methods("GET") |
| srv.apiEndpoints.AddHandlers(r, api.DoNotAddProtectedEndpoints) |
| } |
| |
| // AddMiddleware implements baseapp.App. |
| func (srv *server) AddMiddleware() []mux.MiddlewareFunc { |
| return []mux.MiddlewareFunc{} |
| } |
| |
| func (srv *server) startInternalServer() { |
| // Internal endpoints that are only accessible from within the cluster. |
| internal := mux.NewRouter() |
| srv.apiEndpoints.AddHandlers(internal, api.AddProtectedEndpoints) |
| go func() { |
| sklog.Fatal(http.ListenAndServe(*internalPort, internal)) |
| }() |
| } |
| |
| func main() { |
| baseapp.Serve(New, []string{"scrap.skia.org"}) |
| } |