A REST API for the DN42 registry, written in Go, to provide a bridge between interactive applications and the registry. https://explorer.burble.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
5.4 KiB

// DN42 Registry API Server
package main
import (
log "github.com/sirupsen/logrus"
flag "github.com/spf13/pflag"
// simple event bus
type NotifyFunc func(...interface{})
type SimpleEventBus map[string][]NotifyFunc
var EventBus = make(SimpleEventBus)
// add a listener to an event
func (bus SimpleEventBus) Listen(event string, nfunc NotifyFunc) {
bus[event] = append(bus[event], nfunc)
// fire notifications for an event
func (bus SimpleEventBus) Fire(event string, params ...interface{}) {
funcs := bus[event]
if funcs != nil {
for _, nfunc := range funcs {
// utility func for returning JSON from an API endpoint
func ResponseJSON(w http.ResponseWriter, v interface{}) {
// for response time testing
// marshal the JSON string
data, err := json.Marshal(v)
if err != nil {
"error": err,
}).Error("Failed to marshal JSON")
// write back to http handler
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
// utility function to set the log level
func setLogLevel(levelStr string) {
if level, err := log.ParseLevel(levelStr); err != nil {
// failed to set the level
// set a sensible default and, of course, log the error
"loglevel": levelStr,
"error": err,
}).Error("Failed to set requested log level")
} else {
// set the requested level
// http request logger
func requestLogger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
"method": r.Method,
"URL": r.URL.String(),
"Remote": r.RemoteAddr,
}).Debug("HTTP Request")
next.ServeHTTP(w, r)
// everything starts here
func main() {
// set a default log level, so that logging can be used immediately
// the level will be overidden later on once the command line
// options are loaded
log.Info("DN42 Registry API Server Starting")
// declare cmd line options
var (
logLevel = flag.StringP("LogLevel", "l", "Info", "Log level")
regDir = flag.StringP("RegDir", "d", "registry", "Registry data directory")
bindAddress = flag.StringP("BindAddress", "b", "[::]:8042", "Server bind address")
staticRoot = flag.StringP("StaticRoot", "s", "StaticRoot", "Static page directory")
refreshInterval = flag.StringP("Refresh", "i", "60m", "Refresh interval")
gitPath = flag.StringP("GitPath", "g", "/usr/bin/git", "Path to git executable")
autoPull = flag.BoolP("AutoPull", "a", true, "Automatically pull the registry")
branch = flag.StringP("Branch", "p", "master", "git branch to pull")
authToken = flag.StringP("AuthToken", "t", "secret", "Auth token for refresh endpoint")
// now initialise logging properly based on the cmd line options
// parse the refreshInterval and start data collection
interval, err := time.ParseDuration(*refreshInterval)
if err != nil {
"error": err,
"interval": *refreshInterval,
}).Fatal("Unable to parse registry refresh interval")
InitialiseRegistryData(*regDir, interval,
*gitPath, *autoPull, *branch, *authToken)
// initialise router
router := mux.NewRouter()
// global handers, log all requests and allow compression
// add API routes
subr := router.PathPrefix("/api").Subrouter()
EventBus.Fire("APIEndpoint", subr)
// initialise static routes
InstallStaticRoutes(router, *staticRoot)
// initialise http server
server := &http.Server{
Addr: *bindAddress,
WriteTimeout: time.Second * 15,
ReadTimeout: time.Second * 15,
IdleTimeout: time.Second * 60,
Handler: router,
// run the server in a non-blocking goroutine
"BindAddress": *bindAddress,
}).Info("Starting server")
go func() {
if err := server.ListenAndServe(); err != nil {
"error": err,
"BindAddress": *bindAddress,
}).Fatal("Unable to start server")
// graceful shutdown via SIGINT (^C)
csig := make(chan os.Signal, 1)
signal.Notify(csig, os.Interrupt)
// and block
log.Info("Server shutting down")
// deadline for server to shutdown
ctx, cancel := context.WithTimeout(context.Background(), 10)
defer cancel()
// shutdown the server
// nothing left to do
log.Info("Shutdown complete, all done")
// end of code