package rest_http import ( "bytes" "encoding/json" "fmt" "log" "net/http" "strings" mjson "git.ali33.ru/fcg-xvii/go-tools/json" "git.ali33.ru/fcg-xvii/rest" jwt "github.com/dgrijalva/jwt-go" ) func New(app rest.IApplication, core any, responseHeaders map[string]string) *Rest { return &Rest{ app: app, core: core, responseHeaders: responseHeaders, } } type Rest struct { server rest.IServer app rest.IApplication core any responseHeaders map[string]string } func (s *Rest) App() rest.IApplication { return s.app } func (s *Rest) Prepare(server rest.IServer, httpPrefix string) { server.HandleFunc(httpPrefix, s.handle) s.server = server } func responseNotFound(w http.ResponseWriter) { w.WriteHeader(404) } func responseError(w http.ResponseWriter, err rest.IErrorArgs, code int) { w.WriteHeader(code) w.Write([]byte(err.Error())) } // handle func (s *Rest) handle(w http.ResponseWriter, r *http.Request) { // установка заголовков ответа for k, v := range s.responseHeaders { w.Header().Set(k, v) } // запросы prelight if r.Method == "OPTIONS" { return } // Инициализация rest Request rr := &RequestIn{ RequestIn: &rest.RequestIn{ IRequest: &rest.Request{ Type: rest.RequestTypeIn, Command: r.URL.Path, Data: mjson.Map{}, Files: make(rest.RequestFiles), }, GeneratorToken: s.server.TokenGenerate, Core: s.core, }, } // Парсим Bearer токен и извлекаем claims authHeader := r.Header.Get("Authorization") if authHeader != "" { if parts := strings.Split(authHeader, " "); len(parts) == 2 && parts[0] == "Bearer" { tokenString := parts[1] token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return s.server.Secret(), nil }) if err != nil { log.Printf("Failed to parse JWT: %s", err) http.Error(w, "Invalid token", http.StatusUnauthorized) return } if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { rr.auth = mjson.Map(claims) } } } // Если это многокомпонентный запрос, обрабатываем файлы if strings.Index(r.Header.Get("Content-Type"), "multipart/form-data") == 0 { err := r.ParseMultipartForm(32 << 20) // max memory 32MB, после этого файлы будут сохранены во временных файлах log.Println(err) if err != nil { responseError(w, rest.ErrorMessage("ErrMultipartParse", err.Error()), 500) return } multiPartForm := r.MultipartForm data, check := multiPartForm.Value["data"] if check { rData := rr.RData() err := json.NewDecoder(bytes.NewBuffer([]byte(data[0]))).Decode(&rData) if err != nil { responseError(w, rest.ErrorMessage("ErrMultipartDataParse", err.Error()), 500) return } } files := rr.RFiles() for filename, headers := range multiPartForm.File { for _, header := range headers { file, err := header.Open() if err != nil { err := rest.NewError( "ErrMultipartFileParse", mjson.Map{ "filename": filename, "message": err.Error(), }, ) responseError(w, err, 500) } rlFile := rest.NewReadCloserLen( file, header.Size, ) files[filename] = rlFile } } defer rr.RClose() } else { data := rr.RData() err := json.NewDecoder(r.Body).Decode(&data) if err != nil { responseError(w, rest.ErrorMessage("ErrDataParse", err.Error()), 500) return } } // get command //command, check := s.commands.GetCommand(r.URL.Path) command, check := s.app.Executer(rr) if !check { responseNotFound(w) return } //log.Println(rr.RData) // serialize if err := rest.Serialize(rr.RData(), command); err != nil { responseError(w, err, 500) return } // validate if validator, check := command.(rest.IValidator); check { resp := validator.Validate(rr) if resp != nil { if err := resp.Write(w); err != nil { responseError(w, err, 500) } return } } // execute resp := command.Execute(rr) if err := resp.Write(w); err != nil { responseError(w, err, 500) } resp.RClose() }