@@ -10,6 +10,7 @@ COPY internal internal
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags='-w -s' -o /onasty ./cmd/server -FROM scratch +FROM alpine:3.20 COPY --from=builder /onasty /onasty +RUN apk --no-cache add ca-certificates ENTRYPOINT ["/onasty"]
@@ -7,11 +7,8 @@ includes:
migrate: ./migrations/Taskfile.yml tasks: - build: - - go build -o .bin/onasty ./cmd/server/ - run: - - docker compose up -d --build core + - docker compose up -d --build --remove-orphans core lint: - golangci-lint run@@ -29,7 +26,7 @@ - task: test:unit
- task: test:e2e test:unit: - - go test -v --short ./... + - go test --count=1 -v --short ./... test:e2e: - go test --count=1 -v ./e2e/
@@ -44,7 +44,7 @@ username string
email string password string }{ - {name: "all fiels empty", email: "", password: "", username: ""}, + {name: "all fields empty", email: "", password: "", username: ""}, { name: "non valid email", username: "testing",@@ -99,8 +99,6 @@ }),
) e.Equal(http.StatusCreated, httpResp.Code) - - // TODO: probably should get the token from the email user := e.getLastInsertedUserByEmail(email) token := e.getVerificationTokenByUserID(user.ID)@@ -158,7 +156,7 @@ password: password,
expectedCode: http.StatusBadRequest, }, { - name: "wrong credintials", + name: "wrong credentials", email: email, password: e.uuid(), expectedCode: http.StatusUnauthorized,@@ -235,7 +233,7 @@ expectedMsg: models.ErrUserIsNotActivated.Error(),
}, { name: "wrong email", - email: "wrong@emai.com", + email: "wrong@email.com", password: password, expectedCode: http.StatusUnauthorized, },
@@ -33,7 +33,7 @@ e.require.NoError(err)
} // httpRequest sends http request to the server and returns `httptest.ResponseRecorder` -// conteny-type always set to application/json +// content-type always set to application/json func (e *AppTestSuite) httpRequest( method, url string, body []byte,
@@ -38,6 +38,7 @@ return err
} slog.DebugContext(ctx, "email sent", "subject", subject, "content", content, "err", err) + slog.InfoContext(ctx, "email sent", "to", to) metrics.RecordEmailSent() return nil
@@ -31,7 +31,6 @@ LastLoginAt time.Time
} func (u User) Validate() error { - // NOTE: there's probably a better way to validate emails _, err := mail.ParseAddress(u.Email) if err != nil { return errors.New("user: invalid email") //nolint:err113
@@ -61,7 +61,6 @@ if err != nil {
return dtos.NoteDTO{}, err } - // TODO: there should be a better way to do it m := models.Note{ //nolint:exhaustruct ExpiresAt: note.ExpiresAt, BurnBeforeExpiration: note.BurnBeforeExpiration,@@ -78,6 +77,6 @@ return note, nil
} // TODO: in future not remove, leave some metadata - // to shot user that note was alreasy seen + // to shot user that note was already seen return note, n.noterepo.DeleteBySlug(ctx, note.Slug) }
@@ -10,7 +10,7 @@
var ErrFailedToSendVerifcationEmail = errors.New("failed to send verification email") const ( - verificationEmailSubject = "Onasty: verifiy your email" + verificationEmailSubject = "Onasty: verify your email" verificationEmailBody = `To verify your email, please follow this link: <a href="%[1]s/api/v1/auth/verify/%[2]s">%[1]s/api/v1/auth/verify/%[2]s</a> <br />@@ -24,11 +24,10 @@ cancel context.CancelFunc,
userEmail string, token string, url string, -) error { +) { select { case <-ctx.Done(): - slog.ErrorContext(ctx, "failed to send verfication email", "err", ctx.Err()) - return ErrFailedToSendVerifcationEmail + slog.ErrorContext(ctx, "failed to send verification email", "err", ctx.Err()) default: if err := u.mailer.Send( ctx,@@ -36,10 +35,8 @@ userEmail,
verificationEmailSubject, fmt.Sprintf(verificationEmailBody, url, token), ); err != nil { - return errors.Join(ErrFailedToSendVerifcationEmail, err) + slog.ErrorContext(ctx, "failed to send verification email", "err", err) } cancel() } - - return nil }
@@ -14,6 +14,7 @@ "github.com/olexsmir/onasty/internal/models"
"github.com/olexsmir/onasty/internal/store/psql/sessionrepo" "github.com/olexsmir/onasty/internal/store/psql/userepo" "github.com/olexsmir/onasty/internal/store/psql/vertokrepo" + "github.com/olexsmir/onasty/internal/transport/http/reqid" ) type UserServicer interface {@@ -93,16 +94,8 @@ if err := u.vertokrepo.Create(ctx, vtok, uid, time.Now(), time.Now().Add(u.verificationTokenTTL)); err != nil {
return uuid.Nil, err } - // TODO: handle the error that might be returned - // i dont think that there's need to handle the error, just log it - bgCtx, bgCancel := context.WithTimeout(context.Background(), 10*time.Second) - go u.sendVerificationEmail( //nolint:errcheck,contextcheck - bgCtx, - bgCancel, - inp.Email, - vtok, - u.appURL, - ) + sendingCtx, cancel := getContextForEmailSending(ctx) + go u.sendVerificationEmail(sendingCtx, cancel, inp.Email, vtok, u.appURL) return uid, nil }@@ -223,14 +216,8 @@ if err != nil {
return err } - bgCtx, bgCancel := context.WithTimeout(context.Background(), 10*time.Second) - go u.sendVerificationEmail( //nolint:errcheck,contextcheck - bgCtx, - bgCancel, - inp.Email, - token, - u.appURL, - ) + sendingCtx, cancel := getContextForEmailSending(ctx) + go u.sendVerificationEmail(sendingCtx, cancel, inp.Email, token, u.appURL) return nil }@@ -263,3 +250,11 @@ Access: accessToken,
Refresh: refreshToken, }, err } + +func getContextForEmailSending(ctx context.Context) (context.Context, context.CancelFunc) { + rid := reqid.GetContext(ctx) + resCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + resCtx = reqid.SetContext(resCtx, rid) + + return resCtx, cancel +}
@@ -70,7 +70,7 @@ metrics.RecordFailedRequestMetric(c.Request.Method, c.Request.RequestURI)
} } -//nolint:unused // TODO: remove me later +//nolint:unused func (a *APIV1) isUserAuthorized(c *gin.Context) bool { return !a.getUserID(c).IsNil() }
@@ -48,3 +48,8 @@ return ""
} return rid } + +// SetContext gets a parent context and returns a child context with the set provided request ID +func SetContext(ctx context.Context, rid string) context.Context { + return context.WithValue(ctx, RequestID, rid) +}