add README + golangci config + fix lint

This commit is contained in:
rmanach 2025-01-02 19:09:31 +01:00
parent eede129f84
commit ffa9b52778
5 changed files with 164 additions and 26 deletions

133
.golangci.yml Normal file
View File

@ -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: []

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# librapi
A simple server to store, search and download books.

View File

@ -113,7 +113,7 @@ func postLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore)
http.Error(w, "unexpected error occurred", http.StatusInternalServerError) http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
} }
w.WriteHeader(400) w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, buf.String()) fmt.Fprint(w, buf.String())
return return
} }
@ -128,7 +128,7 @@ func postLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore)
return return
} }
w.WriteHeader(401) w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, buf.String()) fmt.Fprint(w, buf.String())
return return
} }
@ -137,7 +137,9 @@ func postLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore)
if err != nil { if err != nil {
log.Err(err).Msg("unable to create a new session") log.Err(err).Msg("unable to create a new session")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError) http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
} }
cookie := session.GenerateCookie() cookie := session.GenerateCookie()
http.SetCookie(w, cookie) http.SetCookie(w, cookie)

View File

@ -128,8 +128,8 @@ func extractBookForm(r *http.Request) BookForm {
} }
bf.Editor.Value = editor bf.Editor.Value = editor
if a := r.FormValue(bf.Authors.Name); len(a) != 0 { if a := r.FormValue(bf.Authors.Name); a != "" {
bf.Authors.Value = strings.Split(",", a) bf.Authors.Value = strings.Split(a, ",")
} else { } else {
bf.Authors.Err = ErrInvalidAuthors.Error() bf.Authors.Err = ErrInvalidAuthors.Error()
} }
@ -141,8 +141,8 @@ func extractBookForm(r *http.Request) BookForm {
} }
bf.Year.Value = year bf.Year.Value = year
if kw := r.FormValue(bf.Keywords.Name); len(kw) != 0 { if kw := r.FormValue(bf.Keywords.Name); kw != "" {
bf.Keywords.Value = strings.Split(",", kw) bf.Keywords.Value = strings.Split(kw, ",")
} }
file, fileh, err := r.FormFile(bf.File.Name) 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) http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
} }
w.WriteHeader(401) w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, buf.String()) fmt.Fprint(w, buf.String())
return return
} }
@ -192,7 +192,7 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS
} }
if bf.HasErrors() { if bf.HasErrors() {
w.WriteHeader(400) w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, buf.String()) fmt.Fprint(w, buf.String())
return return
} }
@ -202,12 +202,12 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS
dst, err := os.Create(filename) dst, err := os.Create(filename)
if err != nil { 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") log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError) http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
} }
w.WriteHeader(500) w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, buf.String()) fmt.Fprint(w, buf.String())
return return
} }
@ -215,13 +215,13 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS
defer dst.Close() defer dst.Close()
if _, err := io.Copy(dst, bf.File.Value.file); err != nil { 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") log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError) http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return return
} }
w.WriteHeader(500) w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, buf.String()) fmt.Fprint(w, buf.String())
return return
} }
@ -236,7 +236,7 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, s *services.SessionS
fmt.Fprint(w, buf.String()) fmt.Fprint(w, buf.String())
} }
func getUploadFile(w http.ResponseWriter, r *http.Request) { func getUploadFile(w http.ResponseWriter, _ *http.Request) {
uploadForm := templates.GetUploadForm() uploadForm := templates.GetUploadForm()
if uploadForm == nil { if uploadForm == nil {
log.Error().Msg("unable to load upload form") log.Error().Msg("unable to load upload form")

View File

@ -30,12 +30,12 @@ var APISecure = sync.OnceValue[bool](func() bool {
}) })
var ( var (
ErrSessionIdCollision = errors.New("sessionId collision") ErrSessionIDCollision = errors.New("sessionId collision")
ErrUnauthorized = errors.New("unauthorized") ErrUnauthorized = errors.New("unauthorized")
) )
func generateSessionID() (string, error) { func generateSessionID() (string, error) {
sessionID := make([]byte, 32) sessionID := make([]byte, 32) //nolint
if _, err := rand.Read(sessionID); err != nil { if _, err := rand.Read(sessionID); err != nil {
return "", err return "", err
} }
@ -45,7 +45,7 @@ func generateSessionID() (string, error) {
type Session struct { type Session struct {
l sync.RWMutex l sync.RWMutex
sessionId string sessionID string
expirationTime time.Time expirationTime time.Time
} }
@ -55,7 +55,7 @@ func (s *Session) GenerateCookie() *http.Cookie {
return &http.Cookie{ return &http.Cookie{
Name: "session_id", Name: "session_id",
Value: s.sessionId, Value: s.sessionID,
HttpOnly: true, HttpOnly: true,
Secure: APISecure(), Secure: APISecure(),
Expires: s.expirationTime, Expires: s.expirationTime,
@ -97,13 +97,13 @@ func (s *SessionStore) purge() {
} }
for _, session := range toDelete { for _, session := range toDelete {
log.Debug().Str("sessionId", session.sessionId).Msg("purge expired session") log.Debug().Str("sessionId", session.sessionID).Msg("purge expired session")
delete(s.sessions, session.sessionId) delete(s.sessions, session.sessionID)
} }
} }
func (s *SessionStore) purgeWorker() { func (s *SessionStore) purgeWorker() {
ticker := time.NewTicker(10 * time.Second) ticker := time.NewTicker(10 * time.Second) //nolint
go func() { go func() {
for { for {
select { select {
@ -127,7 +127,7 @@ func (s *SessionStore) Done() <-chan struct{} {
} }
func (s *SessionStore) NewSession() (*Session, error) { func (s *SessionStore) NewSession() (*Session, error) {
sessionId, err := generateSessionID() sessionID, err := generateSessionID()
if err != nil { if err != nil {
log.Err(err).Msg("unable to generate sessionId") log.Err(err).Msg("unable to generate sessionId")
return nil, err return nil, err
@ -136,14 +136,14 @@ func (s *SessionStore) NewSession() (*Session, error) {
s.l.Lock() s.l.Lock()
defer s.l.Unlock() defer s.l.Unlock()
if _, ok := s.sessions[sessionId]; ok { if _, ok := s.sessions[sessionID]; ok {
log.Error().Str("sessionId", sessionId).Msg("sessionId collision") log.Error().Str("sessionId", sessionID).Msg("sessionId collision")
return nil, ErrSessionIdCollision return nil, ErrSessionIDCollision
} }
now := time.Now().Add(APISessionExpirationDuration()) now := time.Now().Add(APISessionExpirationDuration())
session := Session{expirationTime: now, sessionId: sessionId} session := Session{expirationTime: now, sessionID: sessionID}
s.sessions[sessionId] = &session s.sessions[sessionID] = &session
return &session, nil return &session, nil
} }