Сборщик курсов валют из нескольких источников

Код-ревьюMiddle30 минпроверка — Спринт23-t001

ТЗ: собирать курсы валют из нескольких внешних источников. Программа каждую минуту отправляет запрос к каждому источнику, получает курс нужной валютной пары и сохраняет результат в БД. Источников может быть несколько, запросы к ним должны идти асинхронно, программа должна корректно завершаться (graceful shutdown).

Проведите код-ревью приложенного кода и отрефакторьте его.

Как на собесе: 30 минут, ищи всё — гонки, утечки, ошибки, безопасность, стиль. Ссылайся на строки.

review.gogo
1package main2 3import (4	"database/sql"5	"fmt"6	"io"7	"net/http"8	"os"9	"strconv"10	"strings"11)12 13type source struct {14	name string15	from string16	to   string17	url  string18}19 20func main() {21	if len(os.Args) != 2 || os.Args[1] != "run" {22		fmt.Println("usage: ./rates run")23		return24	}25 26	sources := []source{27		{name: "src-a", from: "RUB", to: "USD", url: "http://provider-a.example.com/rate"},28		{name: "src-b", from: "RUB", to: "USD", url: "http://provider-b.example.com/rate"},29	}30 31	client := &http.Client{}32	for _, s := range sources {33		req, _ := http.NewRequest(http.MethodGet, s.url, nil)34		resp, err := client.Do(req)35		if err != nil {36			panic(err)37		}38		defer resp.Body.Close()39 40		body, _ := io.ReadAll(resp.Body)41		raw := string(body)42		if s.name == "src-a" {43			raw = strings.ReplaceAll(raw, ",", ".")44		}45 46		value, err := strconv.ParseFloat(raw, 64)47		if err != nil {48			panic(err)49		}50 51		if err := saveRate(s.name, s.from, s.from, value); err != nil {52			panic(err)53		}54	}55}56 57const (58	dbHost = "localhost"59	dbUser = "app"60	dbPass = "app"61	dbName = "rates"62)63 64func saveRate(source, from, to string, value float64) error {65	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=disable", dbHost, dbUser, dbPass, dbName)66 67	db, err := sql.Open("postgres", dsn)68	if err != nil {69		panic(err)70	}71	defer db.Close()72 73	if err := db.Ping(); err != nil {74		panic(err)75	}76	fmt.Println("connected")77 78	q := fmt.Sprintf(`insert into rates (source, "from", "to", value) values ('%s', '%s', '%s', %.4f)`, source, from, to, value)79	_, err = db.Exec(q)80	return err81}
⌘ Enter — проверить