package main import ( "context" "encoding/json" "fmt" "github.com/caarlos0/env/v8" "github.com/google/uuid" "github.com/gorilla/mux" "gorm.io/driver/postgres" "gorm.io/gorm" "log" "net/http" "os" "os/signal" "syscall" ) type Config struct { Port string `env:"USER_HTTP_ADDR" envDefault:"13998"` PgPort string `env:"PG_PORT" envDefault:"5432"` PgHost string `env:"PG_HOST" envDefault:"bd2"` PgDBName string `env:"PG_DB_NAME" envDefault:"service2"` PgUser string `env:"PG_USER" envDefault:"postgres"` PgPwd string `env:"PG_PWD" envDefault:"159753"` } func main() { cfg := Config{} if err := env.Parse(&cfg); err != nil { log.Fatalf("failed to retrieve env variables, %v", err) } if err := Run(cfg); err != nil { log.Fatal("error running grpc server ", err) } } type Office struct { Id uuid.UUID `json:"id" gorm:"type:uuid;default:uuid_generate_v4();primaryKey"` Name string `json:"name" gorm:"name"` Adress string `json:"adress" gorm:"adress"` Clercs []Clerc `json:"clercs" gorm:"foreignKey:OfficeId;references:Id;constraint:OnDelete:CASCADE"` } type Clerc struct { Id uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"` Office Office `gorm:"foreignKey:OfficeId"` OfficeId uuid.UUID } func InitDb(cfg Config) (*gorm.DB, error) { dsn := fmt.Sprintf( "host=%s user=%s password=%s dbname=%s port=%s sslmode=disable", cfg.PgHost, cfg.PgUser, cfg.PgPwd, cfg.PgDBName, cfg.PgPort, ) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal("Cannot to Connect DataBase", err) } db.AutoMigrate(&Office{}, &Clerc{}) return gorm.Open(postgres.Open(dsn), &gorm.Config{}) } type OfficeRepository interface { Create(ctx context.Context, o *Office) error Get(ctx context.Context, id uuid.UUID) (*Office, error) GetList(ctx context.Context) ([]*Office, error) Update(ctx context.Context, o *Office) error Delete(ctx context.Context, id uuid.UUID) error } type ClercRepository interface { Create(ctx context.Context, c *Clerc) error GetList(ctx context.Context) ([]*Clerc, error) } type ClercStorage struct { db *gorm.DB } func NewCs(db *gorm.DB) *ClercStorage { return &ClercStorage{ db: db, } } func (cs *ClercStorage) Create(ctx context.Context, clerc *Clerc) error { return cs.db.WithContext(ctx).Create(clerc).Error } func (cs *ClercStorage) GetList(ctx context.Context) ([]*Clerc, error) { clercs := []*Clerc{} err := cs.db.WithContext(ctx).Find(&clercs).Error return clercs, err } type OfficeStorage struct { db *gorm.DB } func NewOs(db *gorm.DB) *OfficeStorage { return &OfficeStorage{ db: db, } } func (os *OfficeStorage) Create(ctx context.Context, clerc *Office) error { return os.db.WithContext(ctx).Create(clerc).Error } func (os *OfficeStorage) Get(ctx context.Context, id uuid.UUID) (*Office, error) { c := new(Office) err := os.db.WithContext(ctx).First(c, id).Error return c, err } func (os *OfficeStorage) GetList(ctx context.Context) ([]*Office, error) { offices := []*Office{} err := os.db.WithContext(ctx).Find(&offices).Error return offices, err } func (os *OfficeStorage) Update(ctx context.Context, office *Office) error { return os.db.WithContext(ctx).Save(office).Error } func (os *OfficeStorage) Delete(ctx context.Context, id uuid.UUID) error { c := new(Office) return os.db.WithContext(ctx).Where("id = ?", id).Delete(c).Error } type Service struct { officerep OfficeRepository clercrep ClercRepository config Config } func NewServ(cfg Config, officerep OfficeRepository, clercrep ClercRepository) *Service { return &Service{ config: cfg, officerep: officerep, clercrep: clercrep, } } func (s *Service) GetHandler() http.Handler { router := mux.NewRouter() router.HandleFunc("/service2/office/post", s.Post).Methods(http.MethodPost) router.HandleFunc("/service2/office/get", s.Get).Methods(http.MethodGet) router.HandleFunc("/service2/office/getall", s.GetAll).Methods(http.MethodGet) router.HandleFunc("/service2/office/put", s.Put).Methods(http.MethodPut) router.HandleFunc("/service2/office/delete", s.Delete).Methods(http.MethodDelete) router.HandleFunc("/service2/office/patch", s.Patch).Methods(http.MethodPatch) return router } type PostRequest struct { Name string `json:"name"` Adress string `json:"adress"` } func (s *Service) Post(w http.ResponseWriter, r *http.Request) { req := &PostRequest{} if err := json.NewDecoder(r.Body).Decode(req); err != nil { log.Println("Не удалось разкодировать запрос", err) w.WriteHeader(http.StatusBadRequest) return } o := Office{ Name: req.Name, Adress: req.Adress, } err := s.officerep.Create(r.Context(), &o) if err != nil { log.Println("Не удалось создать офис", err) w.WriteHeader(http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(o) } type ClercResponse struct { Clercs []ClercReq `json:"clerks"` } type ClercReq struct { Id string `json:"id"` Name string `json:"name"` Post string `json:"post"` OfficeId string `json:"office_id"` } type GetResponse struct { Id string `json:"id"` Name string `json:"name"` Adress string `json:"adress"` Clercs []ClercReq `json:"clercs"` } func (s *Service) Get(w http.ResponseWriter, r *http.Request) { id := r.URL.Query().Get("id") uuid, err := uuid.Parse(id) if err != nil { log.Println("Не удалось конвертировать строку в uuid", err) w.WriteHeader(http.StatusBadRequest) return } o, err := s.officerep.Get(r.Context(), uuid) if err != nil { log.Println("Не удалось получить запись офиса", err) w.WriteHeader(http.StatusNotFound) return } url := "http://nginx/service1/clerc/getall" response, err := http.Get(url) if err != nil { fmt.Println("Error:", err) return } defer response.Body.Close() /*body, err := ioutil.ReadAll(response.Body) if err != nil { log.Println("Не удалось прочитать body из Get response с первого сервиса", err) w.WriteHeader(http.StatusInternalServerError) return }*/ var clercsreq []ClercReq if err := json.NewDecoder(response.Body).Decode(&clercsreq); err != nil { log.Println("Не удалось разпарсить ответ с 1 сервиса", err) w.WriteHeader(http.StatusInternalServerError) return } //var clerksResponse ClercResponse /*err = json.Unmarshal(body, clercsreq) if err != nil { log.Println("Не удалось разпарсить ответ с 1 сервиса", err) w.WriteHeader(http.StatusInternalServerError) return }*/ cs, err := s.clercrep.GetList(r.Context()) if err != nil { log.Println("Не удалось получить записи клерков из бд", err) w.WriteHeader(http.StatusNotFound) return } clercsid := make([]string, len(cs)) for _, c := range cs { if o.Id == c.OfficeId { clercsid = append(clercsid, c.Id.String()) } } clercs := make([]ClercReq, len(o.Clercs)) for _, clercid := range clercsid { for _, c := range clercsreq { if clercid == c.Id { clercs = append(clercs, c) } } } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(&GetResponse{ Id: o.Id.String(), Name: o.Name, Adress: o.Adress, Clercs: clercs, }) } func (s *Service) GetAll(w http.ResponseWriter, r *http.Request) { offices, err := s.officerep.GetList(r.Context()) if err != nil { log.Println("Не удалось получить записи офисов из бд", err) w.WriteHeader(http.StatusNotFound) return } clercs, err := s.clercrep.GetList(r.Context()) if err != nil { log.Println("Не удалось получить записи клерков из бд", err) w.WriteHeader(http.StatusNotFound) return } //log.Println(clercs) for i, o := range offices { for _, c := range clercs { if o.Id == c.OfficeId { offices[i].Clercs = append(offices[i].Clercs, *c) } } } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(offices) } type PatchRequest struct { OfficeId string `json:"office_id"` ClercId string `json:"clerc_id"` } func (s *Service) Patch(w http.ResponseWriter, r *http.Request) { log.Println("Использована функция добавления клерка в офис") req := &PatchRequest{} if err := json.NewDecoder(r.Body).Decode(req); err != nil { log.Println("Не удалось разкодировать запрос", err) w.WriteHeader(http.StatusBadRequest) return } log.Println(req) uuidOffice, err := uuid.Parse(req.OfficeId) if err != nil { log.Println("Не удалось преобразовать строку в uuid", err) w.WriteHeader(http.StatusBadRequest) return } uuidClerc, err := uuid.Parse(req.ClercId) if err != nil { log.Println("Не удалось преобразовать строку в uuid", err) w.WriteHeader(http.StatusBadRequest) return } log.Println(uuidOffice) o, err := s.officerep.Get(r.Context(), uuidOffice) if err != nil { log.Println("Не удалось получить запись офиса из бд", err) w.WriteHeader(http.StatusInternalServerError) return } log.Println(*o) clerc := &Clerc{ Id: uuidClerc, Office: *o, } /*o.Clercs = append(o.Clercs, *clerc) err = s.officerep.Update(r.Context(), o) if err != nil { log.Println("Не удалось обновить информации об офисе") w.WriteHeader(http.StatusInternalServerError) return }*/ err = s.clercrep.Create(r.Context(), clerc) if err != nil { log.Println("Не удалось создать запись о клерке", err) w.WriteHeader(http.StatusBadRequest) return } log.Println(*clerc) w.WriteHeader(http.StatusOK) } type PutRequest struct { Id string `json:"id"` Name string `json:"name"` Adress string `json:"adress"` } func (s *Service) Put(w http.ResponseWriter, r *http.Request) { req := &PutRequest{} if err := json.NewDecoder(r.Body).Decode(req); err != nil { log.Println("Не удалось разкодировать запрос", err) w.WriteHeader(http.StatusBadRequest) return } uuid, err := uuid.Parse(req.Id) if err != nil { log.Println("Не удалось преобразовать строку в uuid", err) w.WriteHeader(http.StatusBadRequest) return } o, err := s.officerep.Get(r.Context(), uuid) if err != nil { log.Println("Не удалось получить запись офиса из бд", err) w.WriteHeader(http.StatusInternalServerError) return } o.Name = req.Name o.Adress = req.Adress err = s.officerep.Update(r.Context(), o) if err != nil { log.Println("Не удалось обновить запись об офисе", err) w.WriteHeader(http.StatusNotFound) return } o2, err := s.officerep.Get(r.Context(), uuid) if err != nil { log.Println("Не удалось получить запись офиса из бд", err) w.WriteHeader(http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(o2) } type DeleteRequest struct { Id string `json:"id"` } func (s *Service) Delete(w http.ResponseWriter, r *http.Request) { req := &DeleteRequest{} if err := json.NewDecoder(r.Body).Decode(req); err != nil { log.Println("Не удалось разкодировать запрос", err) w.WriteHeader(http.StatusBadRequest) return } uuid, err := uuid.Parse(req.Id) if err != nil { log.Println("Не удалось конвертировать строку в uuid", err) w.WriteHeader(http.StatusBadRequest) return } err = s.officerep.Delete(r.Context(), uuid) if err != nil { log.Println("Не удалось удалить запись об офисе", err) w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusOK) } func Run(cfg Config) error { db, err := InitDb(cfg) if err != nil { return err } serv := NewServ(cfg, NewOs(db), NewCs(db)) s := &http.Server{ Addr: ":13998", //"0.0.0.0:%d", cfg.Port), Handler: serv.GetHandler(), } s.SetKeepAlivesEnabled(true) ctx, cancel := context.WithCancel(context.Background()) go func() { log.Printf("starting http server at %d", cfg.Port) if err := s.ListenAndServe(); err != nil { log.Fatal(err) } }() gracefullyShutdown(ctx, cancel, s) return nil } func gracefullyShutdown(ctx context.Context, cancel context.CancelFunc, server *http.Server) { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) defer signal.Stop(ch) <-ch if err := server.Shutdown(ctx); err != nil { log.Print(err) } cancel() }