package webcmd import ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "regexp" "sync" ) var APP_VERSION string = "x.x.x" // Overridden by makefile type App struct { cfg AppConfig rxTaskInfo *regexp.Regexp tasksMtx sync.RWMutex tasks map[string]Task } func NewApp(configPath string) (*App, error) { confBytes, err := ioutil.ReadFile(configPath) if err != nil { return nil, fmt.Errorf("Couldn't open configuration file '%s': %s", configPath, err.Error()) } cfg := AppConfig{} err = json.Unmarshal(confBytes, &cfg) if err != nil { return nil, fmt.Errorf("Invalid configuration file: %s", err.Error()) } return NewAppFromConfig(cfg), nil } func NewAppFromConfig(cfg AppConfig) *App { return &App{ cfg: cfg, rxTaskInfo: regexp.MustCompile(`^/task/([A-Z0-9]+)$`), tasks: make(map[string]Task), } } func (this *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("Server", "webcmd/"+APP_VERSION) if r.Method == "GET" { if r.URL.Path == "/" { this.Serve_Homepage(w) } else if r.URL.Path == "/style.css" { this.Serve_StyleCSS(w) } else if r.URL.Path == "/tasks" { this.Serve_Tasks(w) } else if matches := this.rxTaskInfo.FindStringSubmatch(r.URL.Path); len(matches) == 2 { this.Serve_TaskInfo(w, matches[1]) } else { http.Error(w, "No matching route for request", 404) } } else if r.Method == "POST" { if r.URL.Path == "/x-new-task" { this.Action_NewTask(w, r) } else if r.URL.Path == "/x-abandon-task" { this.Action_AbandonTask(w, r) } else if r.URL.Path == "/x-clear-completed-tasks" { this.Action_ClearCompleted(w, r) } else { http.Error(w, "No matching route for request", 404) } } else { http.Error(w, "Invalid method", 400) } } func (this *App) Run() { mux := http.NewServeMux() mux.Handle("/", this) log.Printf("Listening on '%s'...", this.cfg.ListenAddress) err := http.ListenAndServe(this.cfg.ListenAddress, mux) if err != nil { log.Fatalf("Network error: %s", err.Error()) } }