diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..fe617bb --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,133 @@ +linters-settings: + # depguard: // Specific for golangci repository + # list-type: blacklist + # packages: + # # logging is allowed only by logutils.Log, logrus + # # is allowed to use only in logutils package + # - github.com/sirupsen/logrus + # packages-with-error-message: + # - github.com/sirupsen/logrus: 'logging is allowed only by logutils.Log' + dupl: + threshold: 100 + funlen: + lines: 100 + statements: 50 + gci: + sections: + prefix(fetchsysd) + goconst: + min-len: 2 + min-occurrences: 2 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - ifElseChain + - octalLiteral + # - whyNoLint + - wrapperFunc + gocyclo: + min-complexity: 15 + goimports: + local-prefixes: localenv + mnd: + # don't include the "operation" and "assign" + checks: + - argument + - case + - condition + - return + govet: + shadow: true + # settings: // Specific for golangci repository + # printf: + # funcs: + # - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + # - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + # - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + # - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + lll: + line-length: 200 + maligned: + suggest-new: true + misspell: + locale: US + nolintlint: + allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space) + allow-unused: false # report any unused nolint directives + require-explanation: false # don't require an explanation for nolint directives + require-specific: false # don't require nolint directives to be specific about which linter is being skipped + errcheck: + check-blank: true + exclude-functions: + - '(*github.com/gin-gonic/gin.Error).SetType' + - '(*github.com/gin-gonic/gin.Context).Error' + +linters: + disable-all: true + enable: + - bodyclose + # - deadcode # deprecated (since v1.49.0) + # - depguard + - dogsled + - dupl + - errcheck + - copyloopvar + - exhaustive + - funlen + - gochecknoinits + - goconst + - gocritic + - gocyclo + - gofmt + - goimports + - mnd + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - lll + - misspell + - nakedret + - noctx + - nolintlint + # - rowserrcheck # https://github.com/golangci/golangci-lint/issues/2649 + - staticcheck + # - structcheck # https://github.com/golangci/golangci-lint/issues/2649 + - stylecheck + - typecheck + - unconvert + - unparam + - unused + # - varcheck # deprecated (since v1.49.0) + - whitespace + # - gochecknoglobals # too many global in ds9 + + # don't enable: + # - asciicheck + # - scopelint + # - gocognit + # - godot + # - godox + # - goerr113 + # - interfacer + # - maligned + # - nestif + # - prealloc + # - testpackage + # - revive + # - wsl + +# issues: +# Excluding configuration per-path, per-linter, per-text and per-source +# fix: true + +run: + timeout: 5m + skip-dirs: [] diff --git a/README.md b/README.md new file mode 100644 index 0000000..4f27c10 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# librapi + +A simple server to store, search and download books. \ No newline at end of file diff --git a/handlers/login/handler.go b/handlers/login/handler.go index d6e56c7..ecbff85 100644 --- a/handlers/login/handler.go +++ b/handlers/login/handler.go @@ -113,7 +113,7 @@ func postLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore) http.Error(w, "unexpected error occurred", http.StatusInternalServerError) } - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, buf.String()) return } @@ -128,7 +128,7 @@ func postLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore) return } - w.WriteHeader(401) + w.WriteHeader(http.StatusUnauthorized) fmt.Fprint(w, buf.String()) return } @@ -137,7 +137,9 @@ func postLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore) if err != nil { log.Err(err).Msg("unable to create a new session") http.Error(w, "unexpected error occurred", http.StatusInternalServerError) + return } + cookie := session.GenerateCookie() http.SetCookie(w, cookie) diff --git a/handlers/upload/handler.go b/handlers/upload/handler.go index fb0c0ad..19899b2 100644 --- a/handlers/upload/handler.go +++ b/handlers/upload/handler.go @@ -128,8 +128,8 @@ func extractBookForm(r *http.Request) BookForm { } bf.Editor.Value = editor - if a := r.FormValue(bf.Authors.Name); len(a) != 0 { - bf.Authors.Value = strings.Split(",", a) + if a := r.FormValue(bf.Authors.Name); a != "" { + bf.Authors.Value = strings.Split(a, ",") } else { bf.Authors.Err = ErrInvalidAuthors.Error() } @@ -141,8 +141,8 @@ func extractBookForm(r *http.Request) BookForm { } bf.Year.Value = year - if kw := r.FormValue(bf.Keywords.Name); len(kw) != 0 { - bf.Keywords.Value = strings.Split(",", kw) + if kw := r.FormValue(bf.Keywords.Name); kw != "" { + bf.Keywords.Value = strings.Split(kw, ",") } file, fileh, err := r.FormFile(bf.File.Name) @@ -179,7 +179,7 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS http.Error(w, "unexpected error occurred", http.StatusInternalServerError) } - w.WriteHeader(401) + w.WriteHeader(http.StatusUnauthorized) fmt.Fprint(w, buf.String()) return } @@ -192,7 +192,7 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS } if bf.HasErrors() { - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, buf.String()) return } @@ -202,12 +202,12 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS dst, err := os.Create(filename) if err != nil { - if err := uploadForm.Execute(buf, &BookForm{Error: "unexpected error occured while creating file"}); err != nil { + if err := uploadForm.Execute(buf, &BookForm{Error: "unexpected error occurred while creating file"}); err != nil { log.Err(err).Msg("unable to generate template") http.Error(w, "unexpected error occurred", http.StatusInternalServerError) } - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) fmt.Fprint(w, buf.String()) return } @@ -215,13 +215,13 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS defer dst.Close() if _, err := io.Copy(dst, bf.File.Value.file); err != nil { - if err := uploadForm.Execute(buf, &BookForm{Error: "unexpected error occured while uploading file"}); err != nil { + if err := uploadForm.Execute(buf, &BookForm{Error: "unexpected error occurred while uploading file"}); err != nil { log.Err(err).Msg("unable to generate template") http.Error(w, "unexpected error occurred", http.StatusInternalServerError) return } - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) fmt.Fprint(w, buf.String()) return } @@ -236,7 +236,7 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS fmt.Fprint(w, buf.String()) } -func getUploadFile(w http.ResponseWriter, r *http.Request) { +func getUploadFile(w http.ResponseWriter, _ *http.Request) { uploadForm := templates.GetUploadForm() if uploadForm == nil { log.Error().Msg("unable to load upload form") diff --git a/services/sessions.go b/services/sessions.go index e5f7cf6..1349a8a 100644 --- a/services/sessions.go +++ b/services/sessions.go @@ -30,12 +30,12 @@ var APISecure = sync.OnceValue[bool](func() bool { }) var ( - ErrSessionIdCollision = errors.New("sessionId collision") + ErrSessionIDCollision = errors.New("sessionId collision") ErrUnauthorized = errors.New("unauthorized") ) func generateSessionID() (string, error) { - sessionID := make([]byte, 32) + sessionID := make([]byte, 32) //nolint if _, err := rand.Read(sessionID); err != nil { return "", err } @@ -45,7 +45,7 @@ func generateSessionID() (string, error) { type Session struct { l sync.RWMutex - sessionId string + sessionID string expirationTime time.Time } @@ -55,7 +55,7 @@ func (s *Session) GenerateCookie() *http.Cookie { return &http.Cookie{ Name: "session_id", - Value: s.sessionId, + Value: s.sessionID, HttpOnly: true, Secure: APISecure(), Expires: s.expirationTime, @@ -97,13 +97,13 @@ func (s *SessionStore) purge() { } for _, session := range toDelete { - log.Debug().Str("sessionId", session.sessionId).Msg("purge expired session") - delete(s.sessions, session.sessionId) + log.Debug().Str("sessionId", session.sessionID).Msg("purge expired session") + delete(s.sessions, session.sessionID) } } func (s *SessionStore) purgeWorker() { - ticker := time.NewTicker(10 * time.Second) + ticker := time.NewTicker(10 * time.Second) //nolint go func() { for { select { @@ -127,7 +127,7 @@ func (s *SessionStore) Done() <-chan struct{} { } func (s *SessionStore) NewSession() (*Session, error) { - sessionId, err := generateSessionID() + sessionID, err := generateSessionID() if err != nil { log.Err(err).Msg("unable to generate sessionId") return nil, err @@ -136,14 +136,14 @@ func (s *SessionStore) NewSession() (*Session, error) { s.l.Lock() defer s.l.Unlock() - if _, ok := s.sessions[sessionId]; ok { - log.Error().Str("sessionId", sessionId).Msg("sessionId collision") - return nil, ErrSessionIdCollision + if _, ok := s.sessions[sessionID]; ok { + log.Error().Str("sessionId", sessionID).Msg("sessionId collision") + return nil, ErrSessionIDCollision } now := time.Now().Add(APISessionExpirationDuration()) - session := Session{expirationTime: now, sessionId: sessionId} - s.sessions[sessionId] = &session + session := Session{expirationTime: now, sessionID: sessionID} + s.sessions[sessionID] = &session return &session, nil }