all repos

onasty @ 11e5f7b

a one-time notes service
5 files changed, 33 insertions(+), 33 deletions(-)
refactor: endpoints should not be pointer receiver (#217)

Author: Oleksandr Smirnov olexsmir@gmail.com
Committed by: GitHub noreply@github.com
Committed at: 2025-09-30 19:08:52 +0300
Parent: ca622f3
M internal/transport/http/apiv1/apiv1.go
···
        41
        41
         	}

      
        42
        42
         }

      
        43
        43
         

      
        44
        
        -func (a *APIV1) Routes(r *gin.RouterGroup) {

      
        
        44
        +func (a APIV1) Routes(r *gin.RouterGroup) {

      
        45
        45
         	r.Use(a.metricsMiddleware)

      
        46
        46
         

      
        47
        47
         	r.GET("/me", a.authorizedMiddleware, a.getMeHandler)

      ···
        95
        95
         	}

      
        96
        96
         }

      
        97
        97
         

      
        98
        
        -func (a *APIV1) slowRateLimit() gin.HandlerFunc {

      
        
        98
        +func (a APIV1) slowRateLimit() gin.HandlerFunc {

      
        99
        99
         	return ratelimit.MiddlewareWithConfig(a.slowRatelimitCfg)

      
        100
        100
         }

      
M internal/transport/http/apiv1/auth.go
···
        14
        14
         	Password string `json:"password"`

      
        15
        15
         }

      
        16
        16
         

      
        17
        
        -func (a *APIV1) signUpHandler(c *gin.Context) {

      
        
        17
        +func (a APIV1) signUpHandler(c *gin.Context) {

      
        18
        18
         	var req signUpRequest

      
        19
        19
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        20
        20
         		invalidRequest(c)

      ···
        44
        44
         	RefreshToken string `json:"refresh_token"`

      
        45
        45
         }

      
        46
        46
         

      
        47
        
        -func (a *APIV1) signInHandler(c *gin.Context) {

      
        
        47
        +func (a APIV1) signInHandler(c *gin.Context) {

      
        48
        48
         	var req signInRequest

      
        49
        49
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        50
        50
         		invalidRequest(c)

      ···
        70
        70
         	RefreshToken string `json:"refresh_token"`

      
        71
        71
         }

      
        72
        72
         

      
        73
        
        -func (a *APIV1) refreshTokensHandler(c *gin.Context) {

      
        
        73
        +func (a APIV1) refreshTokensHandler(c *gin.Context) {

      
        74
        74
         	var req refreshTokenRequest

      
        75
        75
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        76
        76
         		invalidRequest(c)

      ···
        93
        93
         	RefreshToken string `json:"refresh_token"`

      
        94
        94
         }

      
        95
        95
         

      
        96
        
        -func (a *APIV1) logOutHandler(c *gin.Context) {

      
        
        96
        +func (a APIV1) logOutHandler(c *gin.Context) {

      
        97
        97
         	var req logoutRequest

      
        98
        98
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        99
        99
         		invalidRequest(c)

      ···
        112
        112
         	c.Status(http.StatusNoContent)

      
        113
        113
         }

      
        114
        114
         

      
        115
        
        -func (a *APIV1) logOutAllHandler(c *gin.Context) {

      
        
        115
        +func (a APIV1) logOutAllHandler(c *gin.Context) {

      
        116
        116
         	if err := a.authsrv.LogoutAll(c.Request.Context(), a.getUserID(c)); err != nil {

      
        117
        117
         		errorResponse(c, err)

      
        118
        118
         		return

      ···
        123
        123
         

      
        124
        124
         const oatuhStateCookie = "oauth_state"

      
        125
        125
         

      
        126
        
        -func (a *APIV1) oauthLoginHandler(c *gin.Context) {

      
        
        126
        +func (a APIV1) oauthLoginHandler(c *gin.Context) {

      
        127
        127
         	redirectInfo, err := a.authsrv.GetOAuthURL(c.Param("provider"))

      
        128
        128
         	if err != nil {

      
        129
        129
         		errorResponse(c, err)

      ···
        143
        143
         	c.Redirect(http.StatusSeeOther, redirectInfo.URL)

      
        144
        144
         }

      
        145
        145
         

      
        146
        
        -func (a *APIV1) oauthCallbackHandler(c *gin.Context) {

      
        
        146
        +func (a APIV1) oauthCallbackHandler(c *gin.Context) {

      
        147
        147
         	redURL, err := url.Parse(a.frontendURL + "/oauth/callback")

      
        148
        148
         	if err != nil {

      
        149
        149
         		errorResponse(c, err)

      ···
        174
        174
         	c.Redirect(http.StatusFound, redURL.String())

      
        175
        175
         }

      
        176
        176
         

      
        177
        
        -func (a *APIV1) oauthCallbackErrorResponse(c *gin.Context, u *url.URL) {

      
        
        177
        +func (a APIV1) oauthCallbackErrorResponse(c *gin.Context, u *url.URL) {

      
        178
        178
         	u.RawQuery = url.Values{"error": {"internal server error"}}.Encode()

      
        179
        179
         	c.Redirect(http.StatusFound, u.String())

      
        180
        180
         }

      
M internal/transport/http/apiv1/middleware.go
···
        17
        17
         // and if so sets user metadata to context

      
        18
        18
         //

      
        19
        19
         // being authorized is required for making the request for specific endpoint

      
        20
        
        -func (a *APIV1) authorizedMiddleware(c *gin.Context) {

      
        
        20
        +func (a APIV1) authorizedMiddleware(c *gin.Context) {

      
        21
        21
         	token, ok := getTokenFromAuthHeaders(c)

      
        22
        22
         	if !ok {

      
        23
        23
         		errorResponse(c, ErrUnauthorized)

      ···
        39
        39
         // if so sets user metadata to context

      
        40
        40
         //

      
        41
        41
         // it is NOT required to be authorized for making the request for specific endpoint

      
        42
        
        -func (a *APIV1) couldBeAuthorizedMiddleware(c *gin.Context) {

      
        
        42
        +func (a APIV1) couldBeAuthorizedMiddleware(c *gin.Context) {

      
        43
        43
         	token, ok := getTokenFromAuthHeaders(c)

      
        44
        44
         	if ok {

      
        45
        45
         		uid, err := a.validateAuthorizedUser(c.Request.Context(), token)

      ···
        54
        54
         	c.Next()

      
        55
        55
         }

      
        56
        56
         

      
        57
        
        -func (a *APIV1) metricsMiddleware(c *gin.Context) {

      
        
        57
        +func (a APIV1) metricsMiddleware(c *gin.Context) {

      
        58
        58
         	start := time.Now()

      
        59
        59
         	c.Next()

      
        60
        60
         	latency := time.Since(start)

      ···
        92
        92
         // getting user id is only possible if user is authorized

      
        93
        93
         //

      
        94
        94
         // if userID is not set, [uuid.Nil] will be returned.

      
        95
        
        -func (a *APIV1) getUserID(c *gin.Context) uuid.UUID {

      
        
        95
        +func (a APIV1) getUserID(c *gin.Context) uuid.UUID {

      
        96
        96
         	userID, exists := c.Get(userIDCtxKey)

      
        97
        97
         	if !exists {

      
        98
        98
         		return uuid.Nil

      ···
        106
        106
         	return uid

      
        107
        107
         }

      
        108
        108
         

      
        109
        
        -func (a *APIV1) validateAuthorizedUser(ctx context.Context, accessToken string) (uuid.UUID, error) {

      
        
        109
        +func (a APIV1) validateAuthorizedUser(ctx context.Context, accessToken string) (uuid.UUID, error) {

      
        110
        110
         	tokenPayload, err := a.authsrv.ParseJWTToken(accessToken)

      
        111
        111
         	if err != nil {

      
        112
        112
         		return uuid.Nil, err

      
M internal/transport/http/apiv1/note.go
···
        21
        21
         	Slug string `json:"slug"`

      
        22
        22
         }

      
        23
        23
         

      
        24
        
        -func (a *APIV1) createNoteHandler(c *gin.Context) {

      
        
        24
        +func (a APIV1) createNoteHandler(c *gin.Context) {

      
        25
        25
         	var req createNoteRequest

      
        26
        26
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        27
        27
         		invalidRequest(c)

      ···
        53
        53
         	ExpiresAt            time.Time `json:"expires_at,omitzero"`

      
        54
        54
         }

      
        55
        55
         

      
        56
        
        -func (a *APIV1) getNoteBySlugHandler(c *gin.Context) {

      
        
        56
        +func (a APIV1) getNoteBySlugHandler(c *gin.Context) {

      
        57
        57
         	note, err := a.notesrv.GetBySlugAndRemoveIfNeeded(

      
        58
        58
         		c.Request.Context(),

      
        59
        59
         		notesrv.GetNoteBySlugInput{

      ···
        84
        84
         	Password string `json:"password"`

      
        85
        85
         }

      
        86
        86
         

      
        87
        
        -func (a *APIV1) getNoteBySlugAndPasswordHandler(c *gin.Context) {

      
        
        87
        +func (a APIV1) getNoteBySlugAndPasswordHandler(c *gin.Context) {

      
        88
        88
         	var req getNoteBuySlugAndPasswordRequest

      
        89
        89
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        90
        90
         		invalidRequest(c)

      ···
        122
        122
         	HasPassword bool      `json:"has_password"`

      
        123
        123
         }

      
        124
        124
         

      
        125
        
        -func (a *APIV1) getNoteMetadataByIDHandler(c *gin.Context) {

      
        
        125
        +func (a APIV1) getNoteMetadataByIDHandler(c *gin.Context) {

      
        126
        126
         	meta, err := a.notesrv.GetNoteMetadataBySlug(c.Request.Context(), c.Param("slug"))

      
        127
        127
         	if err != nil {

      
        128
        128
         		errorResponse(c, err)

      ···
        145
        145
         	ReadAt               time.Time `json:"read_at,omitzero"`

      
        146
        146
         }

      
        147
        147
         

      
        148
        
        -func (a *APIV1) getNotesHandler(c *gin.Context) {

      
        
        148
        +func (a APIV1) getNotesHandler(c *gin.Context) {

      
        149
        149
         	notes, err := a.notesrv.GetAllByAuthorID(c.Request.Context(), a.getUserID(c))

      
        150
        150
         	if err != nil {

      
        151
        151
         		errorResponse(c, err)

      ···
        155
        155
         	c.JSON(http.StatusOK, mapNotesDTOToResponse(notes))

      
        156
        156
         }

      
        157
        157
         

      
        158
        
        -func (a *APIV1) getReadNotesHandler(c *gin.Context) {

      
        
        158
        +func (a APIV1) getReadNotesHandler(c *gin.Context) {

      
        159
        159
         	notes, err := a.notesrv.GetAllReadByAuthorID(c.Request.Context(), a.getUserID(c))

      
        160
        160
         	if err != nil {

      
        161
        161
         		errorResponse(c, err)

      ···
        165
        165
         	c.JSON(http.StatusOK, mapNotesDTOToResponse(notes))

      
        166
        166
         }

      
        167
        167
         

      
        168
        
        -func (a *APIV1) getUnReadNotesHandler(c *gin.Context) {

      
        
        168
        +func (a APIV1) getUnReadNotesHandler(c *gin.Context) {

      
        169
        169
         	notes, err := a.notesrv.GetAllUnreadByAuthorID(c.Request.Context(), a.getUserID(c))

      
        170
        170
         	if err != nil {

      
        171
        171
         		errorResponse(c, err)

      ···
        180
        180
         	KeepBeforeExpiration *bool      `json:"keep_before_expiration,omitempty"`

      
        181
        181
         }

      
        182
        182
         

      
        183
        
        -func (a *APIV1) updateNoteHandler(c *gin.Context) {

      
        
        183
        +func (a APIV1) updateNoteHandler(c *gin.Context) {

      
        184
        184
         	var req updateNoteRequest

      
        185
        185
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        186
        186
         		invalidRequest(c)

      ···
        203
        203
         	c.Status(http.StatusOK)

      
        204
        204
         }

      
        205
        205
         

      
        206
        
        -func (a *APIV1) deleteNoteHandler(c *gin.Context) {

      
        
        206
        +func (a APIV1) deleteNoteHandler(c *gin.Context) {

      
        207
        207
         	if err := a.notesrv.DeleteBySlug(

      
        208
        208
         		c.Request.Context(),

      
        209
        209
         		c.Param("slug"),

      ···
        220
        220
         	Password string `json:"password"`

      
        221
        221
         }

      
        222
        222
         

      
        223
        
        -func (a *APIV1) setNotePasswordHandler(c *gin.Context) {

      
        
        223
        +func (a APIV1) setNotePasswordHandler(c *gin.Context) {

      
        224
        224
         	var req setNotePasswordRequest

      
        225
        225
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        226
        226
         		invalidRequest(c)

      
M internal/transport/http/apiv1/user.go
···
        15
        15
         	NotesCreated int       `json:"notes_created"`

      
        16
        16
         }

      
        17
        17
         

      
        18
        
        -func (a *APIV1) getMeHandler(c *gin.Context) {

      
        
        18
        +func (a APIV1) getMeHandler(c *gin.Context) {

      
        19
        19
         	uinfo, err := a.usersrv.GetUserInfo(c.Request.Context(), a.getUserID(c))

      
        20
        20
         	if err != nil {

      
        21
        21
         		errorResponse(c, err)

      ···
        35
        35
         	NewPassword     string `json:"new_password"`

      
        36
        36
         }

      
        37
        37
         

      
        38
        
        -func (a *APIV1) changePasswordHandler(c *gin.Context) {

      
        
        38
        +func (a APIV1) changePasswordHandler(c *gin.Context) {

      
        39
        39
         	var req changePasswordRequest

      
        40
        40
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        41
        41
         		invalidRequest(c)

      ···
        60
        60
         	Email string `json:"email"`

      
        61
        61
         }

      
        62
        62
         

      
        63
        
        -func (a *APIV1) requestResetPasswordHandler(c *gin.Context) {

      
        
        63
        +func (a APIV1) requestResetPasswordHandler(c *gin.Context) {

      
        64
        64
         	var req requestResetPasswordRequest

      
        65
        65
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        66
        66
         		invalidRequest(c)

      ···
        84
        84
         	Password string `json:"password"`

      
        85
        85
         }

      
        86
        86
         

      
        87
        
        -func (a *APIV1) resetPasswordHandler(c *gin.Context) {

      
        
        87
        +func (a APIV1) resetPasswordHandler(c *gin.Context) {

      
        88
        88
         	var req resetPasswordRequest

      
        89
        89
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        90
        90
         		invalidRequest(c)

      ···
        109
        109
         	NewEmail string `json:"new_email"`

      
        110
        110
         }

      
        111
        111
         

      
        112
        
        -func (a *APIV1) requestEmailChangeHandler(c *gin.Context) {

      
        
        112
        +func (a APIV1) requestEmailChangeHandler(c *gin.Context) {

      
        113
        113
         	var req changeEmailRequest

      
        114
        114
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        115
        115
         		invalidRequest(c)

      ···
        129
        129
         	c.Status(http.StatusOK)

      
        130
        130
         }

      
        131
        131
         

      
        132
        
        -func (a *APIV1) changeEmailHandler(c *gin.Context) {

      
        
        132
        +func (a APIV1) changeEmailHandler(c *gin.Context) {

      
        133
        133
         	if err := a.usersrv.ChangeEmail(

      
        134
        134
         		c.Request.Context(),

      
        135
        135
         		c.Param("token"),

      ···
        141
        141
         	c.String(http.StatusOK, "email changed")

      
        142
        142
         }

      
        143
        143
         

      
        144
        
        -func (a *APIV1) verifyHandler(c *gin.Context) {

      
        
        144
        +func (a APIV1) verifyHandler(c *gin.Context) {

      
        145
        145
         	if err := a.usersrv.Verify(

      
        146
        146
         		c.Request.Context(),

      
        147
        147
         		c.Param("token"),

      ···
        157
        157
         	Email string `json:"email"`

      
        158
        158
         }

      
        159
        159
         

      
        160
        
        -func (a *APIV1) resendVerificationEmailHandler(c *gin.Context) {

      
        
        160
        +func (a APIV1) resendVerificationEmailHandler(c *gin.Context) {

      
        161
        161
         	var req resendVerificationEmailRequest

      
        162
        162
         	if err := c.ShouldBindJSON(&req); err != nil {

      
        163
        163
         		invalidRequest(c)