builds!
This commit is contained in:
131
process/formless.go
Normal file
131
process/formless.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.eeqj.de/sneak/formless/database"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"github.com/k0kubun/pp"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// CLIEntry is the main entrypoint
|
||||
func CLIEntry(version string, buildarch string) int {
|
||||
f := new(Formless)
|
||||
f.version = version
|
||||
f.buildarch = buildarch
|
||||
f.configure()
|
||||
f.setupLogging()
|
||||
f.setupDatabase()
|
||||
return f.runForever()
|
||||
}
|
||||
|
||||
// Feta is the main structure/process of this app
|
||||
type Formless struct {
|
||||
version string
|
||||
buildarch string
|
||||
api *Server
|
||||
dbm *database.Manager
|
||||
startup time.Time
|
||||
}
|
||||
|
||||
func (f *Formless) configure() {
|
||||
viper.SetConfigName("formless")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath("/etc/formless") // path to look for the config file in
|
||||
viper.AddConfigPath("$HOME/.config/formless") // call multiple times to add many search paths
|
||||
|
||||
viper.SetEnvPrefix("FORMLESS")
|
||||
viper.AutomaticEnv()
|
||||
|
||||
viper.SetDefault("Debug", false)
|
||||
viper.SetDefault("DBURL", fmt.Sprintf("sqlite://%s", os.ExpandEnv("$HOME/Library/ApplicationSupport/formless/formless.db")))
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||
// Config file not found; ignore error if desired
|
||||
} else {
|
||||
// Config file was found but another error was produced
|
||||
log.Panic().
|
||||
Err(err).
|
||||
Msg("cannot read config file")
|
||||
}
|
||||
}
|
||||
|
||||
if viper.GetBool("debug") {
|
||||
pp.Print(viper.AllSettings())
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Formless) identify() {
|
||||
log.Info().
|
||||
Str("version", f.version).
|
||||
Str("buildarch", f.buildarch).
|
||||
Msg("starting")
|
||||
}
|
||||
|
||||
func (f *Formless) setupDatabase() {
|
||||
f.dbm = database.New()
|
||||
}
|
||||
|
||||
func (f *Formless) setupLogging() {
|
||||
|
||||
log.Logger = log.With().Caller().Logger()
|
||||
|
||||
tty := isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
|
||||
|
||||
if tty {
|
||||
out := zerolog.NewConsoleWriter(
|
||||
func(w *zerolog.ConsoleWriter) {
|
||||
// Customize time format
|
||||
w.TimeFormat = time.RFC3339
|
||||
},
|
||||
)
|
||||
log.Logger = log.Output(out)
|
||||
}
|
||||
|
||||
// always log in UTC
|
||||
zerolog.TimestampFunc = func() time.Time {
|
||||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
if viper.GetBool("debug") {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
|
||||
f.identify()
|
||||
}
|
||||
|
||||
func (f *Formless) uptime() time.Duration {
|
||||
return time.Since(f.startup)
|
||||
}
|
||||
|
||||
func (f *Formless) runForever() int {
|
||||
f.startup = time.Now()
|
||||
|
||||
home := os.Getenv("HOME")
|
||||
if home == "" {
|
||||
panic("can't find home directory")
|
||||
}
|
||||
|
||||
f.api = new(Server)
|
||||
f.api.SetFormless(f) // api needs to get to us to access data
|
||||
|
||||
go f.api.Serve()
|
||||
|
||||
// this goroutine (main) does nothing until we handle signals
|
||||
// maybe use a context or something
|
||||
// FIXME(sneak)
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
30
process/handlers.go
Normal file
30
process/handlers.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/flosch/pongo2"
|
||||
"github.com/labstack/echo"
|
||||
)
|
||||
|
||||
type hash map[string]interface{}
|
||||
|
||||
func (a *Server) notFoundHandler(c echo.Context) error {
|
||||
return c.String(http.StatusNotFound, "404 not found")
|
||||
}
|
||||
|
||||
func (a *Server) indexHandler(c echo.Context) error {
|
||||
tc := pongo2.Context{}
|
||||
return c.Render(http.StatusOK, "index.html", tc)
|
||||
}
|
||||
|
||||
func (a *Server) healthCheckHandler(c echo.Context) error {
|
||||
resp := &hash{
|
||||
"status": "ok",
|
||||
"now": time.Now().UTC().Format(time.RFC3339),
|
||||
"uptime": a.formless.uptime().String(),
|
||||
}
|
||||
return c.JSONPretty(http.StatusOK, resp, " ")
|
||||
|
||||
}
|
||||
109
process/server.go
Normal file
109
process/server.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
gl "github.com/labstack/gommon/log"
|
||||
ep2 "github.com/mayowa/echo-pongo2"
|
||||
"github.com/ziflex/lecho"
|
||||
|
||||
//"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Server is the HTTP webserver object
|
||||
type Server struct {
|
||||
formless *Formless
|
||||
port uint
|
||||
e *echo.Echo
|
||||
router *gin.Engine
|
||||
httpserver *http.Server
|
||||
debug bool
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func (a *Server) SetFormless(f *Formless) {
|
||||
a.formless = f
|
||||
}
|
||||
|
||||
func (a *Server) Serve() {
|
||||
|
||||
if a.formless == nil {
|
||||
panic("must have app from which to serve")
|
||||
}
|
||||
|
||||
if os.Getenv("DEBUG") != "" {
|
||||
a.debug = true
|
||||
}
|
||||
|
||||
a.port = 8080
|
||||
|
||||
if os.Getenv("PORT") != "" {
|
||||
s, err := strconv.ParseUint(os.Getenv("PORT"), 10, 64)
|
||||
if err != nil {
|
||||
a.port = uint(s)
|
||||
}
|
||||
}
|
||||
|
||||
a.initRouter()
|
||||
a.initServer()
|
||||
a.e.Logger.Fatal(a.e.StartServer(a.httpserver))
|
||||
}
|
||||
|
||||
func (s *Server) initRouter() {
|
||||
|
||||
// Echo instance
|
||||
s.e = echo.New()
|
||||
|
||||
s.e.HideBanner = true
|
||||
|
||||
lev := gl.INFO
|
||||
if os.Getenv("DEBUG") != "" {
|
||||
lev = gl.DEBUG
|
||||
}
|
||||
|
||||
logger := lecho.New(
|
||||
os.Stdout,
|
||||
lecho.WithLevel(lev),
|
||||
lecho.WithTimestamp(),
|
||||
lecho.WithCaller(),
|
||||
)
|
||||
s.e.Logger = logger
|
||||
s.e.Use(middleware.RequestID())
|
||||
|
||||
// Middleware
|
||||
s.e.Use(middleware.Logger())
|
||||
s.e.Use(middleware.Recover())
|
||||
|
||||
r, err := ep2.NewRenderer("view")
|
||||
if err != nil {
|
||||
s.e.Logger.Fatal(err)
|
||||
}
|
||||
s.e.Renderer = r
|
||||
|
||||
// Routes
|
||||
s.e.GET("/", s.indexHandler)
|
||||
s.e.GET("/.well-known/healthcheck.json", s.healthCheckHandler)
|
||||
// FIXME add prometheus metrics output
|
||||
//a.e.GET("/about", s.aboutHandler)
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) initServer() {
|
||||
log.Info().Uint("port", s.port).Msg("starting webserver")
|
||||
|
||||
s.httpserver = &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", s.port),
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user