func main() { defer mustbe.Catched(func(err error) { log.Fatalln("Fatal error:", err) }) var ( botToken string apiHost string dbFileName string ) flag.StringVar(&botToken, "token", "", "telegram bot token") flag.StringVar(&apiHost, "apihost", "freefeed.net", "backend API host") flag.StringVar(&dbFileName, "dbfile", "", "database file name") flag.Parse() if botToken == "" || dbFileName == "" { flag.Usage() return } db := mustbe.OKVal(bolt.Open(dbFileName, 0600, &bolt.Options{Timeout: 1 * time.Second})).(*bolt.DB) defer db.Close() mustbe.OK(db.Update(func(tx *bolt.Tx) error { mustbe.OKVal(tx.CreateBucketIfNotExists(StatesBucket)) return nil })) bot := mustbe.OKVal(tgbotapi.NewBotAPI(botToken)).(*tgbotapi.BotAPI) u := tgbotapi.NewUpdate(0) u.Timeout = 60 updates, err := bot.GetUpdatesChan(u) if err != nil { log.Fatalln("Can not get update chan:", err) return } log.Println("Starting bot", bot.Self.UserName) app := &App{ db: db, apiHost: apiHost, outbox: make(chan tgbotapi.Chattable, 0), rts: make(map[int]*Realtime), cache: gcache.New(1000).ARC().Build(), } app.LoadRT() for { select { case update := <-updates: go app.HandleMessage(&update.Message) case msg := <-app.outbox: bot.Send(msg) } } }
func ExampleOK() { defer mustbe.Catched(func(err error) { fmt.Println("Catched", err) }) err := errors.New("sample error") mustbe.OK(err) fmt.Println("Will not be printed") // Output: Catched sample error }
func main() { defer mustbe.Catched(func(err error) { log.Println("Fatal error:", err) os.Exit(1) }) mustbeTrue(len(os.Args) >= 2, "Usage: frf-aprox conf.json") app := new(App) mustbe.OK(app.Load(os.Args[1])) s := &http.Server{ Addr: app.Listen, Handler: http.HandlerFunc(app.Handler), ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } log.Printf("Starting server at %s\n", app.Listen) mustbe.OK(s.ListenAndServe()) }
func (a *App) Handler(w http.ResponseWriter, r *http.Request) { defer mustbe.Catched(func(err error) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(H{"err": err.Error(), "from": "aprx"}) }) // request path: /frf-domain/api-path pp := strings.SplitN(r.URL.Path, "/", 3)[1:] mustbeTrue(len(pp) == 2, "Invalid request path") domain, path := pp[0], "/"+pp[1] domainFound := false for _, d := range a.FRFDomains { if d == domain { domainFound = true break } } mustbeTrue(domainFound, "Invalid frf domain") var ( accessToken string frfRequestBody []byte ) switch r.Header.Get("Content-Type") { case "application/x-www-form-urlencoded": r.ParseForm() accessToken = r.PostForm.Get("accessToken") r.PostForm.Del("accessToken") frfRequestBody = []byte(r.PostForm.Encode()) case "application/json", "application/json; charset=utf-8": h := make(H) mustbe.OK(json.NewDecoder(r.Body).Decode(h)) accessToken = h["accessToken"].(string) delete(h, "accessToken") frfRequestBody = mustbe.OKVal(json.Marshal(h)).([]byte) } req, err := http.NewRequest( r.Method, "https://"+domain+path, bytes.NewReader(frfRequestBody), ) mustbe.OK(err) for _, h := range HeadersFromClient { if hh, ok := r.Header[h]; ok { req.Header[h] = hh } } if accessToken != "" { req.Header.Set("X-Authentication-Token", accessToken) } resp, err := http.DefaultClient.Do(req) mustbe.OK(err) defer resp.Body.Close() for _, h := range HeadersFromBackend { if hh, ok := resp.Header[h]; ok { w.Header()[h] = hh } } w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) }
func mustbeTrue(test bool, errMsg string) { if !test { mustbe.OK(errors.New(errMsg)) } }
func (a *App) PostPublicParam(r *http.Request) (respCode int, respValue interface{}) { defer mustbe.Catched(func(err error) { a.Log.WithField("error", err).Error(err.Error()) respCode, respValue = http.StatusInternalServerError, "Internal server error" }) req := &struct { AuthToken string `json:"authToken"` Value json.RawMessage `json:"value"` }{} pathVars := mux.Vars(r) site, username, paramName := pathVars["site"], pathVars["username"], pathVars["paramName"] pProps := a.PublicParams[paramName] siteInfo := a.Sites[site] if err := json.NewDecoder(io.LimitReader(r.Body, int64(pProps.MaxRequestSize))).Decode(req); err != nil { return http.StatusBadRequest, err.Error() } // валидность данных val, err := pProps.Schema.Validate(gojsonschema.NewStringLoader(string(req.Value))) mustbe.OK(err) if !val.Valid() { return http.StatusBadRequest, val.Errors()[0].Description() } api := ffapi.New(siteInfo.APIRoot, req.AuthToken) whoami, err := api.WhoAmI() mustbe.OK(err) if whoami.Users.Username != username { // возможно, редактируется группа uinfo, err := api.UserInfo(username) mustbe.OK(err) adminFound := false for _, adm := range uinfo.Admins { if adm.ID == whoami.Users.ID { adminFound = true break } } if !adminFound { return http.StatusBadRequest, "You can not manage this account" } } // всё в порядке mustbe.OK(a.DB.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(PublicBucketName)) return b.Put( []byte(site+"/"+username+"/"+paramName), []byte(req.Value), ) })) return http.StatusOK, nil }