func handleRegisterWorker(workerManager *client.WorkerManager, w http.ResponseWriter, r *http.Request) { info := client.WorkerInfo{ Id: r.FormValue("id"), Article: bitwrk.ArticleId(r.FormValue("article")), Method: "http-push", PushURL: r.FormValue("pushurl"), } if r.Method != "POST" || info.Id == "" || info.PushURL == "" { registerWorkerTemplate.Execute(w, info) } workerManager.RegisterWorker(info) }
func handleGrantMandate(r *http.Request) error { var mandate client.Mandate mandate.Identity = BitcoinIdentity if r.FormValue("type") == "BUY" { mandate.BidType = bitwrk.Buy } else if r.FormValue("type") == "SELL" { mandate.BidType = bitwrk.Sell } else { return fmt.Errorf("Illegal trade type: %v", r.FormValue("type")) } mandate.Article = bitwrk.ArticleId(r.FormValue("articleid")) if err := mandate.Price.Parse(r.FormValue("price")); err != nil { return err } mandate.UseTradesLeft = "on" == r.FormValue("usetradesleft") mandate.UseUntil = "on" == r.FormValue("usevaliduntil") if n, err := strconv.ParseInt(r.FormValue("tradesleft"), 10, 32); err != nil { return fmt.Errorf("Illegal value for trades left: %v", err) } else if n <= 0 { return fmt.Errorf("Number of trades left must be positive, but is: %v", n) } else { mandate.TradesLeft = int(n) } if n, err := strconv.ParseInt(r.FormValue("validminutes"), 10, 32); err != nil { return fmt.Errorf("Illegal value for minutes left: %v", err) } else if n <= 0 { return fmt.Errorf("Number of minutes left must be positive, but is: %v", n) } else { mandate.Until = time.Now().Add(time.Duration(n) * time.Minute) } if !mandate.UseTradesLeft && !mandate.UseUntil { mandate.UseTradesLeft = true mandate.TradesLeft = 1 } key := client.GetActivityManager().NewKey() client.GetActivityManager().RegisterMandate(key, &mandate) return nil }
func handleBuy(w http.ResponseWriter, r *http.Request) { article := r.URL.Path[5:] log.Printf("Handling buy for %#v from %v", article, r.RemoteAddr) if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var buy *client.BuyActivity if _buy, err := client.GetActivityManager().NewBuy(bitwrk.ArticleId(article)); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Printf("Error creating buy activity: %v", err) return } else { buy = _buy } defer buy.Dispose() log := bitwrk.Root().Newf("Buy #%v", buy.GetKey()) var reader io.Reader if multipart, err := r.MultipartReader(); err != nil { // read directly from body reader = r.Body } else { // Iterate through parts of multipart body, find the one called "data" for { if part, err := multipart.NextPart(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Printf("Error iterating through multipart content: %v", err) return } else { if part.FormName() == "data" { reader = part break } else { log.Printf("Skipping form part %v", part) } } } } workTemp := client.GetActivityManager().GetStorage().Create(fmt.Sprintf("buy #%v: work", buy.GetKey())) defer workTemp.Dispose() if _, err := io.Copy(workTemp, reader); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Printf("Error receiving work data from client: %v", err) return } else { if err := workTemp.Close(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Printf("Error writing work data to storage: %v", err) return } } // Listen for close notfications interrupt := w.(http.CloseNotifier).CloseNotify() workFile := workTemp.File() defer workFile.Dispose() var result cafs.File if res, err := buy.PerformBuy(log, interrupt, workFile); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Printf("Error receiving result from BitWrk network: %v", err) return } else { result = res } http.Redirect(w, r, "/file/"+result.Key().String(), http.StatusSeeOther) }
// Query for a list of transactions. Admin-only for now. func handleQueryTrades(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) if !user.IsAdmin(c) { http.Error(w, "Action requires admin privileges", http.StatusForbidden) return } w.Header().Set("Access-Control-Allow-Origin", "*") limitStr := r.FormValue("limit") var limit int if limitStr == "" { limit = 1000 } else if n, err := strconv.ParseUint(limitStr, 10, 14); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { limit = int(n) } articleStr := r.FormValue("article") var article bitwrk.ArticleId if articleStr == "" { http.Error(w, "article argument missing", http.StatusNotFound) return } else if err := checkArticle(c, articleStr); err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } else { article = bitwrk.ArticleId(articleStr) } periodStr := r.FormValue("period") if periodStr == "" { periodStr = "1d" } else if !resolutionExists(periodStr) { http.Error(w, "period unknown", http.StatusNotFound) return } period := resolutionByName(periodStr) // If begin is not given, calculate beginStr := r.FormValue("begin") var begin time.Time if beginStr == "" { begin = time.Now().Add(-period.interval) } else if t, err := time.Parse(time.RFC3339, beginStr); err != nil { http.Error(w, "Invalid begin time", http.StatusNotFound) return } else { begin = t } end := begin.Add(period.interval) unit := money.MustParseUnit("mBTC") buffer := new(bytes.Buffer) fmt.Fprintf(buffer, "{\"begin\": %#v, \"end\": %#v, \"unit\": \"%v\", \"data\": [\n", begin.Format(time.RFC3339), end.Format(time.RFC3339), unit) count := 0 priceSum := money.MustParse("BTC 0") feeSum := money.MustParse("BTC 0") firstLine := true handler := func(key string, tx bitwrk.Transaction) { var comma string if firstLine { firstLine = false comma = " " } else { comma = "," } fmt.Fprintf(buffer, "%v[% 5d, %#v, %v, %v, %v, \"%v\", \"%v\", %#v]\n", comma, count, tx.Matched.Format(time.RFC3339Nano), tx.Matched.UnixNano()/1000000, tx.Price.Format(unit, false), tx.Fee.Format(unit, false), tx.State, tx.Phase, key) priceSum = priceSum.Add(tx.Price) feeSum = feeSum.Add(tx.Fee) count++ } if err := db.QueryTransactions(c, limit, article, begin, end, handler); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) c.Errorf("Error querying transactions: %v", err) return } fmt.Fprintf(buffer, "], \"price_sum\": %v, \"fee_sum\": %v}\n", priceSum.Format(unit, false), feeSum.Format(unit, false)) // Write result back to requester w.Header().Set("Content-Type", "application/json") buffer.WriteTo(w) }
func handleQueryPrices(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") c := appengine.NewContext(r) needLogin := false articleStr := r.FormValue("article") var article bitwrk.ArticleId if articleStr == "" { http.Error(w, "article argument missing", http.StatusNotFound) return } else if err := checkArticle(c, articleStr); err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } else { article = bitwrk.ArticleId(articleStr) } periodStr := r.FormValue("period") if periodStr == "" { periodStr = "1d" } else if !resolutionExists(periodStr) { http.Error(w, "period unknown", http.StatusNotFound) return } period := resolutionByName(periodStr) resolutionStr := r.FormValue("resolution") if resolutionStr == "" { resolutionStr = "3m" } else if !resolutionExists(resolutionStr) { http.Error(w, "resolution unknown", http.StatusNotFound) return } resolution := resolutionByName(resolutionStr) if period.interval > 10000*resolution.interval { // User requested a lot of data... restrict for now needLogin = true } // If begin is not given, calculate from period beginStr := r.FormValue("begin") var begin time.Time if beginStr == "" { begin = time.Now().Add(-period.interval) } else if t, err := time.Parse(time.RFC3339, beginStr); err != nil { http.Error(w, "Invalid begin time", http.StatusNotFound) return } else { begin = t // Explicitly stating begin requires athentication for now needLogin = true } unitStr := r.FormValue("unit") var unit money.Unit if unitStr == "" { unit = money.MustParseUnit("mBTC") } else if u, err := money.ParseUnit(unitStr); err != nil { http.Error(w, "Invalid unit parameter", http.StatusNotFound) } else { unit = u } // Calculate end from begin and period end := begin.Add(period.interval) // Lookup coarsest tile resolution tile := resolution.coarsestTileResolution() if period.finerThan(tile) { tile = period } if tile.finerThan(resolution.finestTileResolution()) { tile = resolution.finestTileResolution() } // Truncate begin to a multiple of coarsest tile resolution. begin = begin.Truncate(tile.interval) // Enforce admin permissions if necessary if needLogin && !user.IsAdmin(c) { http.Error(w, "Action requires admin privileges", http.StatusForbidden) return } if prices, err := queryPrices(c, article, tile, resolution, begin, end); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else if r.FormValue("format") == "flot" { w.Header().Set("Content-Type", "application/json") renderPricesForFlot(w, prices, unit) } else if data, err := json.Marshal(prices); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { // Write result back to requester w.Header().Set("Content-Type", "application/json") w.Write(data) } }