goBlockchainDataAnalysis/serverRoutes.go
serverRoutes.goRaw
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "net/http"
7 "sort"
8 "strconv"
9 "time"
10
11 "gopkg.in/mgo.v2/bson"
12
13 "github.com/gorilla/mux"
14)
15
16type Routes []Route
17
18var routes = Routes{
19 Route{
20 "Index",
21 "GET",
22 "/",
23 Index,
24 },
25 Route{
26 "Stats",
27 "Get",
28 "/stats",
29 Stats,
30 },
31 Route{
32 "AllAddresses",
33 "Get",
34 "/alladdresses",
35 AllAddresses,
36 },
37 Route{
38 "Blocks",
39 "Get",
40 "/blocks/{page}/{count}",
41 Blocks,
42 },
43 Route{
44 "Txs",
45 "Get",
46 "/txs/{page}/{count}",
47 Txs,
48 },
49 Route{
50 "Addresses",
51 "Get",
52 "/addresses/{page}/{count}",
53 Addresses,
54 },
55 Route{
56 "Block",
57 "GET",
58 "/block/{height}",
59 Block,
60 },
61 Route{
62 "Tx",
63 "GET",
64 "/tx/{txid}",
65 Tx,
66 },
67 Route{
68 "Address",
69 "GET",
70 "/address/{hash}",
71 Address,
72 },
73 Route{
74 "AddressNetwork",
75 "GET",
76 "/address/network/{address}",
77 AddressNetwork,
78 },
79 Route{
80 "BlockSankey",
81 "GET",
82 "/block/{height}/sankey",
83 BlockSankey,
84 },
85 Route{
86 "TxSankey",
87 "GET",
88 "/tx/{txid}/sankey",
89 TxSankey,
90 },
91 Route{
92 "AddressSankey",
93 "GET",
94 "/address/sankey/{address}",
95 AddressSankey,
96 },
97 Route{
98 "NetworkMap",
99 "Get",
100 "/map",
101 NetworkMap,
102 },
103 Route{
104 "GetTotalHourAnalysis",
105 "Get",
106 "/totalhouranalysis",
107 GetTotalHourAnalysis,
108 },
109 Route{
110 "GetLast24HourAnalysis",
111 "Get",
112 "/last24hour",
113 GetLast24HourAnalysis,
114 },
115 Route{
116 "GetLast7DayAnalysis",
117 "Get",
118 "/last7day",
119 GetLast7DayAnalysis,
120 },
121 Route{
122 "GetLast7DayHourAnalysis",
123 "Get",
124 "/last7dayhour",
125 GetLast7DayHourAnalysis,
126 },
127 Route{
128 "GetLastMonthsAnalysis",
129 "Get",
130 "/lastmonths/{count}",
131 GetLastMonthsAnalysis,
132 },
133 Route{
134 "GetAddressTimeChart",
135 "GET",
136 "/addresstimechart/{hash}",
137 GetAddressTimeChart,
138 },
139}
140
141//ROUTES
142
143func Index(w http.ResponseWriter, r *http.Request) {
144 fmt.Fprintln(w, "ask for recommendations in /r")
145 //http.FileServer(http.Dir("./web"))
146}
147
148/*
149func NewUser(w http.ResponseWriter, r *http.Request) {
150 ipFilter(w, r)
151 decoder := json.NewDecoder(r.Body)
152 var newUser UserModel
153 err := decoder.Decode(&newUser)
154 check(err)
155 defer r.Body.Close()
156
157 saveUser(userCollection, newUser)
158
159 fmt.Println(newUser)
160 fmt.Fprintln(w, "new user added: ", newUser.ID)
161}
162*/
163func Stats(w http.ResponseWriter, r *http.Request) {
164 ipFilter(w, r)
165
166 stats := getStats()
167
168 jsonResp, err := json.Marshal(stats)
169 check(err)
170
171 fmt.Fprintln(w, string(jsonResp))
172}
173func AllAddresses(w http.ResponseWriter, r *http.Request) {
174 ipFilter(w, r)
175
176 nodes := []NodeModel{}
177 iter := nodeCollection.Find(bson.M{"type": "address"}).Limit(10000).Iter()
178 err := iter.All(&nodes)
179
180 //convert []resp struct to json
181 jsonNodes, err := json.Marshal(nodes)
182 check(err)
183
184 fmt.Fprintln(w, string(jsonNodes))
185}
186func Blocks(w http.ResponseWriter, r *http.Request) {
187 ipFilter(w, r)
188 vars := mux.Vars(r)
189 page, err := strconv.Atoi(vars["page"])
190 check(err)
191 count, err := strconv.Atoi(vars["count"])
192 check(err)
193
194 blocks := []BlockModel{}
195 err = blockCollection.Find(bson.M{}).Skip((page - 1) * 20).Limit(count).Sort("-$natural").All(&blocks)
196 check(err)
197
198 for _, block := range blocks {
199 //blockheight := strconv.FormatInt(block.Height, 10)
200 blockheight := block.Height
201 txs := []TxModel{}
202 err = txCollection.Find(bson.M{"blockheight": blockheight}).All(&txs)
203 block.Txs = txs
204 }
205
206 //convert []resp struct to json
207 jsonData, err := json.Marshal(blocks)
208 check(err)
209
210 fmt.Fprintln(w, string(jsonData))
211}
212func Txs(w http.ResponseWriter, r *http.Request) {
213 ipFilter(w, r)
214 vars := mux.Vars(r)
215 page, err := strconv.Atoi(vars["page"])
216 check(err)
217 count, err := strconv.Atoi(vars["count"])
218 check(err)
219
220 txs := []TxModel{}
221 err = txCollection.Find(bson.M{}).Skip((page - 1) * 20).Limit(count).Sort("-$natural").All(&txs)
222 check(err)
223
224 //convert []resp struct to json
225 jsonData, err := json.Marshal(txs)
226 check(err)
227
228 fmt.Fprintln(w, string(jsonData))
229}
230func Addresses(w http.ResponseWriter, r *http.Request) {
231 ipFilter(w, r)
232 vars := mux.Vars(r)
233 page, err := strconv.Atoi(vars["page"])
234 check(err)
235 count, err := strconv.Atoi(vars["count"])
236 check(err)
237
238 addresses := []AddressModel{}
239 err = addressCollection.Find(bson.M{}).Skip((page - 1) * 20).Limit(count).Sort("-$natural").All(&addresses)
240 check(err)
241
242 //convert []resp struct to json
243 jsonResp, err := json.Marshal(addresses)
244 check(err)
245
246 fmt.Fprintln(w, string(jsonResp))
247}
248func Block(w http.ResponseWriter, r *http.Request) {
249 ipFilter(w, r)
250
251 vars := mux.Vars(r)
252 var heightString string
253 heightString = vars["height"]
254 height, err := strconv.ParseInt(heightString, 10, 64)
255 if err != nil {
256 fmt.Fprintln(w, "not valid height")
257 } else {
258 block := BlockModel{}
259 err := blockCollection.Find(bson.M{"height": height}).One(&block)
260
261 txs := []TxModel{}
262 err = txCollection.Find(bson.M{"blockheight": heightString}).All(&txs)
263 block.Txs = txs
264
265 //convert []resp struct to json
266 jsonResp, err := json.Marshal(block)
267 check(err)
268
269 fmt.Fprintln(w, string(jsonResp))
270 }
271}
272func Tx(w http.ResponseWriter, r *http.Request) {
273 ipFilter(w, r)
274
275 vars := mux.Vars(r)
276 txid := vars["txid"]
277 if txid == "undefined" {
278 fmt.Fprintln(w, "not valid txid")
279 } else {
280 tx := TxModel{}
281 err := txCollection.Find(bson.M{"txid": txid}).One(&tx)
282
283 //convert []resp struct to json
284 jsonResp, err := json.Marshal(tx)
285 check(err)
286
287 fmt.Fprintln(w, string(jsonResp))
288 }
289}
290func Address(w http.ResponseWriter, r *http.Request) {
291 ipFilter(w, r)
292
293 vars := mux.Vars(r)
294 hash := vars["hash"]
295 if hash == "undefined" {
296 fmt.Fprintln(w, "not valid hash")
297 } else {
298 address := AddressModel{}
299 err := addressCollection.Find(bson.M{"hash": hash}).One(&address)
300
301 txs := []TxModel{}
302 err = txCollection.Find(bson.M{"$or": []bson.M{bson.M{"vin.address": hash}, bson.M{"vout.address": hash}}}).All(&txs)
303 address.Txs = txs
304
305 for _, tx := range address.Txs {
306 blocks := []BlockModel{}
307 err = blockCollection.Find(bson.M{"hash": tx.BlockHash}).All(&blocks)
308 for _, block := range blocks {
309 address.Blocks = append(address.Blocks, block)
310 }
311 }
312
313 //convert []resp struct to json
314 jsonResp, err := json.Marshal(address)
315 check(err)
316
317 fmt.Fprintln(w, string(jsonResp))
318 }
319}
320func AddressNetwork(w http.ResponseWriter, r *http.Request) {
321 ipFilter(w, r)
322
323 vars := mux.Vars(r)
324 address := vars["address"]
325 if address == "undefined" {
326 fmt.Fprintln(w, "not valid address")
327 } else {
328 network := addressTree(address)
329 network.Nodes[0].Shape = "triangle"
330
331 //convert []resp struct to json
332 jNetwork, err := json.Marshal(network)
333 check(err)
334
335 fmt.Fprintln(w, string(jNetwork))
336 }
337}
338func BlockSankey(w http.ResponseWriter, r *http.Request) {
339 ipFilter(w, r)
340 vars := mux.Vars(r)
341 var heightString string
342 heightString = vars["height"]
343 height, err := strconv.ParseInt(heightString, 10, 64)
344 if err != nil {
345 fmt.Fprintln(w, "not valid height")
346 } else {
347 block := BlockModel{}
348 err := blockCollection.Find(bson.M{"height": height}).One(&block)
349
350 txs := []TxModel{}
351 err = txCollection.Find(bson.M{"blockheight": heightString}).All(&txs)
352 block.Txs = txs
353
354 var nodesCount int
355 mapNodeK := make(map[string]int)
356 var sankey SankeyModel
357 for _, tx := range block.Txs {
358 var sankeyNodeA SankeyNodeModel
359 sankeyNodeA.Node = nodesCount
360 mapNodeK["tx"] = nodesCount
361 nodesCount++
362 sankeyNodeA.Name = "tx"
363 sankey.Nodes = append(sankey.Nodes, sankeyNodeA)
364
365 for _, vin := range tx.Vin {
366 var sankeyNode SankeyNodeModel
367 sankeyNode.Node = nodesCount
368 mapNodeK[vin.Address] = nodesCount
369 nodesCount++
370 sankeyNode.Name = vin.Address
371 sankey.Nodes = append(sankey.Nodes, sankeyNode)
372
373 var sankeyLink SankeyLinkModel
374 sankeyLink.Source = mapNodeK[vin.Address]
375 sankeyLink.Target = mapNodeK["tx"]
376 sankeyLink.Value = vin.Amount
377 fmt.Println(sankeyLink)
378 sankey.Links = append(sankey.Links, sankeyLink)
379 fmt.Println(sankey.Links)
380 }
381
382 for _, vout := range tx.Vout {
383 var sankeyNode SankeyNodeModel
384 sankeyNode.Node = nodesCount
385 mapNodeK[vout.Address] = nodesCount
386 nodesCount++
387 sankeyNode.Name = vout.Address
388 sankey.Nodes = append(sankey.Nodes, sankeyNode)
389
390 var sankeyLink SankeyLinkModel
391 sankeyLink.Source = mapNodeK["tx"]
392 sankeyLink.Target = mapNodeK[vout.Address]
393 sankeyLink.Value = vout.Value
394 fmt.Println(sankeyLink)
395 sankey.Links = append(sankey.Links, sankeyLink)
396 }
397
398 }
399
400 fmt.Println("Sankey generated")
401 fmt.Println(sankey)
402
403 //convert []resp struct to json
404 jsonSankey, err := json.Marshal(sankey)
405 check(err)
406
407 fmt.Fprintln(w, string(jsonSankey))
408 }
409}
410func TxSankey(w http.ResponseWriter, r *http.Request) {
411 ipFilter(w, r)
412 vars := mux.Vars(r)
413 txid := vars["txid"]
414 if txid == "undefined" {
415 fmt.Fprintln(w, "not valid height")
416 } else {
417
418 tx := TxModel{}
419 err := txCollection.Find(bson.M{"txid": txid}).One(&tx)
420
421 var nodesCount int
422 mapNodeK := make(map[string]int)
423 var sankey SankeyModel
424 var sankeyNodeA SankeyNodeModel
425 sankeyNodeA.Node = nodesCount
426 mapNodeK["tx"] = nodesCount
427 nodesCount++
428 sankeyNodeA.Name = "tx"
429 sankey.Nodes = append(sankey.Nodes, sankeyNodeA)
430
431 fmt.Println(tx.Vin)
432 for _, vin := range tx.Vin {
433 var sankeyNode SankeyNodeModel
434 sankeyNode.Node = nodesCount
435 mapNodeK[vin.Address] = nodesCount
436 nodesCount++
437 sankeyNode.Name = vin.Address
438 sankey.Nodes = append(sankey.Nodes, sankeyNode)
439
440 var sankeyLink SankeyLinkModel
441 sankeyLink.Source = mapNodeK[vin.Address]
442 sankeyLink.Target = mapNodeK["tx"]
443 sankeyLink.Value = vin.Amount
444 sankey.Links = append(sankey.Links, sankeyLink)
445 }
446
447 for _, vout := range tx.Vout {
448 var sankeyNode SankeyNodeModel
449 sankeyNode.Node = nodesCount
450 mapNodeK[vout.Address] = nodesCount
451 nodesCount++
452 sankeyNode.Name = vout.Address
453 sankey.Nodes = append(sankey.Nodes, sankeyNode)
454
455 var sankeyLink SankeyLinkModel
456 sankeyLink.Source = mapNodeK["tx"]
457 sankeyLink.Target = mapNodeK[vout.Address]
458 sankeyLink.Value = vout.Value
459 sankey.Links = append(sankey.Links, sankeyLink)
460 }
461
462 fmt.Println("Sankey generated")
463
464 //convert []resp struct to json
465 jsonSankey, err := json.Marshal(sankey)
466 check(err)
467
468 fmt.Fprintln(w, string(jsonSankey))
469 }
470}
471func AddressSankey(w http.ResponseWriter, r *http.Request) {
472 ipFilter(w, r)
473 vars := mux.Vars(r)
474 address := vars["address"]
475 if address == "undefined" {
476 fmt.Fprintln(w, "not valid address")
477 } else {
478 network := addressTree(address)
479 var sankey SankeyModel
480
481 fmt.Println("network generated")
482 mapNodeK := make(map[string]int)
483 for k, n := range network.Nodes {
484 var sankeyNode SankeyNodeModel
485 //sankeyNode.StringNode = n.Id
486 sankeyNode.Node = k
487 sankeyNode.Name = n.Id
488 sankey.Nodes = append(sankey.Nodes, sankeyNode)
489 mapNodeK[n.Id] = k
490 }
491 for _, e := range network.Edges {
492 var sankeyLink SankeyLinkModel
493 //sankeyLink.StringSource = e.From
494 sankeyLink.Source = mapNodeK[e.From]
495 //sankeyLink.StringTarget = e.To
496 sankeyLink.Target = mapNodeK[e.To]
497 sankeyLink.Value = e.Label
498 sankey.Links = append(sankey.Links, sankeyLink)
499 }
500 fmt.Println("Sankey generated")
501
502 //convert []resp struct to json
503 jsonSankey, err := json.Marshal(sankey)
504 check(err)
505
506 fmt.Fprintln(w, string(jsonSankey))
507 }
508}
509func NetworkMap(w http.ResponseWriter, r *http.Request) {
510 ipFilter(w, r)
511
512 nodes, err := getAllNodes()
513 check(err)
514 edges, err := getAllEdges()
515 check(err)
516
517 var network NetworkModel
518 network.Nodes = nodes
519 network.Edges = edges
520
521 //convert []resp struct to json
522 jNetwork, err := json.Marshal(network)
523 check(err)
524
525 fmt.Fprintln(w, string(jNetwork))
526}
527
528func GetTotalHourAnalysis(w http.ResponseWriter, r *http.Request) {
529 ipFilter(w, r)
530
531 hourAnalysis := []ChartCountModel{}
532 iter := hourCountCollection.Find(bson.M{}).Limit(10000).Iter()
533 err := iter.All(&hourAnalysis)
534
535 //sort by hour
536 sort.Slice(hourAnalysis, func(i, j int) bool {
537 return hourAnalysis[i].Elem < hourAnalysis[j].Elem
538 })
539
540 var resp ChartAnalysisResp
541 for _, d := range hourAnalysis {
542 resp.Labels = append(resp.Labels, strconv.Itoa(d.Elem))
543 resp.Data = append(resp.Data, d.Count)
544 }
545
546 //convert []resp struct to json
547 jsonResp, err := json.Marshal(resp)
548 check(err)
549 fmt.Fprintln(w, string(jsonResp))
550}
551
552func GetLast24HourAnalysis(w http.ResponseWriter, r *http.Request) {
553 ipFilter(w, r)
554
555 fromDate := time.Now().AddDate(0, 0, -1)
556 toDate := time.Now()
557
558 txs := []TxModel{}
559 err := txCollection.Find(bson.M{
560 "datet": bson.M{
561 "$gt": fromDate,
562 "$lt": toDate,
563 },
564 }).Sort("-$natural").All(&txs)
565 check(err)
566
567 //generate map with 24 hours
568 hourFrequencies := map24hours()
569 for _, tx := range txs {
570 hourFrequencies[tx.Date.Hour]++
571 }
572 var hourCount []ChartCountModel
573 for hour, frequency := range hourFrequencies {
574 hourCount = append(hourCount, ChartCountModel{hour, frequency})
575 }
576 //sort by hour
577 sort.Slice(hourCount, func(i, j int) bool {
578 return hourCount[i].Elem < hourCount[j].Elem
579 })
580
581 var resp ChartAnalysisResp
582 for _, d := range hourCount {
583 resp.Labels = append(resp.Labels, strconv.Itoa(d.Elem))
584 resp.Data = append(resp.Data, d.Count)
585 }
586
587 //convert []resp struct to json
588 jsonResp, err := json.Marshal(resp)
589 check(err)
590 fmt.Fprintln(w, string(jsonResp))
591}
592func GetLast7DayAnalysis(w http.ResponseWriter, r *http.Request) {
593 ipFilter(w, r)
594
595 fromDate := time.Now().AddDate(0, 0, -7)
596 toDate := time.Now()
597
598 txs := []TxModel{}
599 err := txCollection.Find(bson.M{
600 "datet": bson.M{
601 "$gt": fromDate,
602 "$lt": toDate,
603 },
604 }).Sort("-$natural").All(&txs)
605 check(err)
606
607 //generate map with 24 hours
608 //hourFrequencies := map24hours()
609 dayFrequencies := make(map[int]int)
610 for _, tx := range txs {
611 dayFrequencies[tx.Date.Day]++
612 }
613 var dayCount []ChartCountModel
614 for day, frequency := range dayFrequencies {
615 dayCount = append(dayCount, ChartCountModel{day, frequency})
616 }
617 //sort by hour
618 sort.Slice(dayCount, func(i, j int) bool {
619 return dayCount[i].Elem < dayCount[j].Elem
620 })
621
622 var resp ChartAnalysisResp
623 for _, d := range dayCount {
624 resp.Labels = append(resp.Labels, strconv.Itoa(d.Elem))
625 resp.Data = append(resp.Data, d.Count)
626 }
627
628 //convert []resp struct to json
629 jsonResp, err := json.Marshal(resp)
630 check(err)
631 fmt.Fprintln(w, string(jsonResp))
632}
633
634func GetLast7DayHourAnalysis(w http.ResponseWriter, r *http.Request) {
635 ipFilter(w, r)
636
637 var resp ChartSeriesAnalysisResp
638
639 for i := 0; i < 7; i++ {
640 fromDate := time.Now().AddDate(0, 0, -i-1)
641 toDate := time.Now().AddDate(0, 0, -i)
642 txs := []TxModel{}
643 err := txCollection.Find(bson.M{
644 "datet": bson.M{
645 "$gt": fromDate,
646 "$lt": toDate,
647 },
648 }).Sort("-$natural").All(&txs)
649 check(err)
650
651 //generate map with 24 hours
652 hourFrequencies := map24hours()
653 for _, tx := range txs {
654 hourFrequencies[tx.Date.Hour]++
655 }
656 var hourCount []ChartCountModel
657 for hour, frequency := range hourFrequencies {
658 hourCount = append(hourCount, ChartCountModel{hour, frequency})
659 }
660 //sort by hour
661 sort.Slice(hourCount, func(i, j int) bool {
662 return hourCount[i].Elem < hourCount[j].Elem
663 })
664 var dayData []int
665 for _, d := range hourCount {
666 dayData = append(dayData, d.Count)
667 }
668 if len(txs) > 0 {
669 resp.Series = append(resp.Series, txs[0].Date.Day)
670 resp.Data = append(resp.Data, dayData)
671 }
672 }
673 hourLabels := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"}
674 resp.Labels = hourLabels
675
676 //convert []resp struct to json
677 jsonResp, err := json.Marshal(resp)
678 check(err)
679 fmt.Fprintln(w, string(jsonResp))
680}
681func GetLastMonthsAnalysis(w http.ResponseWriter, r *http.Request) {
682 ipFilter(w, r)
683 vars := mux.Vars(r)
684 count, err := strconv.Atoi(vars["count"])
685 check(err)
686 fmt.Println(count)
687 fromDate := time.Now().AddDate(0, -count, 0)
688 toDate := time.Now()
689
690 txs := []TxModel{}
691 err = txCollection.Find(bson.M{
692 "datet": bson.M{
693 "$gt": fromDate,
694 "$lt": toDate,
695 },
696 }).Sort("-$natural").All(&txs)
697 check(err)
698
699 //generate map with 24 hours
700 //hourFrequencies := map24hours()
701 dayFrequencies := make(map[float64]int)
702 for _, tx := range txs {
703 dayFrequencies[float64(tx.Date.Month) + float64(tx.Date.Day)/100]++
704 }
705 var dayCount []ChartCountFloat64Model
706 for day, frequency := range dayFrequencies {
707 dayCount = append(dayCount, ChartCountFloat64Model{day, frequency})
708 }
709 //sort by hour
710 sort.Slice(dayCount, func(i, j int) bool {
711 return dayCount[i].Elem < dayCount[j].Elem
712 })
713
714 var resp ChartAnalysisResp
715 for _, d := range dayCount {
716 resp.Labels = append(resp.Labels, strconv.FormatFloat(d.Elem, 'f', -1, 64))
717 resp.Data = append(resp.Data, d.Count)
718 }
719
720 //convert []resp struct to json
721 jsonResp, err := json.Marshal(resp)
722 check(err)
723 fmt.Fprintln(w, string(jsonResp))
724}
725func GetAddressTimeChart(w http.ResponseWriter, r *http.Request) {
726 ipFilter(w, r)
727
728 vars := mux.Vars(r)
729 hash := vars["hash"]
730 if hash == "undefined" {
731 fmt.Fprintln(w, "not valid hash")
732 } else {
733 address := AddressModel{}
734 err := addressCollection.Find(bson.M{"hash": hash}).One(&address)
735
736 txs := []TxModel{}
737 err = txCollection.Find(bson.M{"$or": []bson.M{bson.M{"vin.address": hash}, bson.M{"vout.address": hash}}}).All(&txs)
738 address.Txs = txs
739
740 for _, tx := range address.Txs {
741 blocks := []BlockModel{}
742 err = blockCollection.Find(bson.M{"hash": tx.BlockHash}).All(&blocks)
743 for _, block := range blocks {
744 address.Blocks = append(address.Blocks, block)
745 }
746 }
747
748 count := make(map[time.Time]float64)
749 for _, tx := range txs {
750 var val float64
751 for _, vin := range tx.Vin {
752 val = val + vin.Amount
753 }
754 count[tx.DateT] = val
755 }
756 var dateSorted []time.Time
757 for t, _ := range count {
758 dateSorted = append(dateSorted, t)
759 }
760 sort.Slice(dateSorted, func(i, j int) bool {
761 //return dateSorted[i] < dateSorted[j]
762 return dateBeforeThan(dateSorted[i], dateSorted[j])
763 })
764
765 var resp ChartAnalysisRespFloat64
766 for _, t := range dateSorted {
767 resp.Labels = append(resp.Labels, t.String())
768 resp.Data = append(resp.Data, count[t])
769 }
770
771 //convert []resp struct to json
772 jsonResp, err := json.Marshal(resp)
773 check(err)
774
775 fmt.Fprintln(w, string(jsonResp))
776 }
777}