package main import ( "context" "flag" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" // ent pure go sqlite3 driver instead of "github.com/mattn/go-sqlite3". _ "github.com/xiaoqidun/entps" "git.dotya.ml/mirre-mt/pcmt/app" "git.dotya.ml/mirre-mt/pcmt/config" "git.dotya.ml/mirre-mt/pcmt/ent" ) const ( banner = ` __ ____ _________ ___ / /_ / __ \/ ___/ __ ` + "`" + `__ \/ __/ / /_/ / /__/ / / / / / /_ / .___/\___/_/ /_/ /_/\__/ %s Password Compromise Monitoring Tool %s /_/` licenseHeader = `pcmt - Password Compromise Monitoring Tool Copyright (C) git.dotya.ml/wanderer This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see .` ) var ( addr = flag.String("addr", ":3000", "TCP address:port to listen at") // compress = flag.Bool("compress", false, "Enable transparent response compression") configFlag = flag.String("config", "config.dhall", "Default path of the config file") devel = flag.Bool("devel", false, "Run the application in dev mode, connect to a local browser-sync instance for hot-reloading") // pages = map[string]string{ // "/": "templates/index.tmpl", // } version = "dev" ) func run() error { printHeader() flag.Parse() // TODO: allow different configuration formats (toml, ni) // TODO: rename main.go to pcmt.go // TODO: add .golangci-lint // TODO: add flake.nix // TODO: connect to postgres // TODO: design user schemas, models. // TODO: SBOM: https://actuated.dev/blog/sbom-in-github-actions // TODO: SBOM: https://www.docker.com/blog/generate-sboms-with-buildkit/ conf, err := config.LoadConfig(*configFlag) if err != nil { log.Println("error loading config file", *configFlag) return err } // for "github.com/xiaoqidun/entps". connstr := "file:ent?mode=memory&cache=shared&_fk=1" log.Printf("connecting to db at '%s'", connstr) db, err := ent.Open("sqlite3", connstr) if err != nil { return fmt.Errorf("failed to open a connection to sqlite: %v", err) } defer db.Close() log.Println("attempting to automatically migrate db schema") // Run the auto migration tool. if err = db.Schema.Create(context.Background()); err != nil { return fmt.Errorf("failed creating schema resources: %v", err) } a := &app.App{} err = a.Init(version, conf, db) if err != nil { return err } a.PrintConfiguration() a.SetupRoutes() logger := a.Logger() a.SetEchoSettings() // a.SetConfig(conf) a.SetEmbeds(templates, assets) if *devel { a.SetDevel() } // // TODO: add check for prometheus config setting. // if true { // // import "github.com/labstack/echo-contrib/prometheus" // p := prometheus.NewPrometheus("echo", nil) // p.Use(e) // } e := a.E() go func() { if err = e.Start(*addr); err != nil && err != http.ErrServerClosed { logger.Println("shutting down the server") } }() quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) signal.Notify(quit, syscall.SIGTERM) signal.Notify(quit, syscall.SIGHUP) <-quit ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer func() { logger.Println("Interrupt received, gracefully shutting down the server") cancel() }() if err = e.Shutdown(ctx); err != nil { return err } return nil } func printHeader() { fmt.Fprintf(os.Stderr, "%s\n\n%s\n\n", licenseHeader, banner) }