package main import ( "encoding/json" "flag" "fmt" "log" "net/http" "os" "strconv" "strings" "time" again "git.rootprojects.org/root/go-again" "git.rootprojects.org/root/go-again/data/jsondb" ) func main() { portEnv := os.Getenv("PORT") dbEnv := os.Getenv("DATABASE_URL") portInt := flag.Int("port", 0, "port on which to serve http") addr := flag.String("addr", "", "address on which to serve http") dburl := flag.String("database-url", "", "For example: json://relative-path/db.json or json:///absolute-path/db.json") flag.Parse() if "" != portEnv { if 0 != *portInt { log.Fatal("You may set PORT or --port, but not both.") return } n, err := strconv.Atoi(portEnv) if nil != err { log.Fatalf("Could not parse PORT=%q.", n) return } *portInt = n } if *portInt < 1024 || *portInt > 65535 { log.Fatalf("`port` should be between 1024 and 65535, not %d.", *portInt) return } portEnv = strconv.Itoa(*portInt) if "" != dbEnv { if "" != *dburl { log.Fatal("You may set DATABASE_URL or --database-url, but not both.") return } *dburl = dbEnv // TODO parse string? // TODO have each connector try in sequence by registering with build tags like go-migrate does? } if "" == *dburl { log.Fatalf("`database-url` must be specified." + " Something like --database-url='json:///var/go-again/db.json' should do nicely.") return } db, err := jsondb.Connect(*dburl) if nil != err { log.Fatalf("Could not connect to database %q: %s", *dburl, err) return } s := &scheduler{ DB: db, } mux := http.NewServeMux() server := &http.Server{ Addr: fmt.Sprintf("%s:%s", *addr, portEnv), Handler: mux, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } //mux.Handle("/api/", http.HandlerFunc(handleFunc)) mux.HandleFunc("/api/v0/schedules", s.Handle) // TODO Filebox FS mux.Handle("/", http.FileServer(http.Dir("./public"))) fmt.Println("Listening on", server.Addr) log.Fatal(server.ListenAndServe()) } type ScheduleDB interface { List() ([]again.Schedule, error) } type scheduler struct { DB ScheduleDB } func (s *scheduler) Handle(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") if "" == token { http.Error(w, "Authorization Header did not contain a valid token", http.StatusForbidden) return } fmt.Println("whatever", r.Method, r.URL) switch r.Method { case http.MethodGet: s.List(w, r) return case http.MethodPost: http.Error(w, "Not Implemented", http.StatusNotImplemented) return default: http.Error(w, "Not Implemented", http.StatusNotImplemented) return } } func (s *scheduler) List(w http.ResponseWriter, r *http.Request) { schedules, err := s.DB.List() if nil != err { http.Error(w, err.Error(), http.StatusInternalServerError) return } buf, err := json.Marshal(schedules) if nil != err { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Write(buf) }