From d9cf0ade7fa8d54d40c8bfcb0efd025309aeda80 Mon Sep 17 00:00:00 2001 From: d3m0k1d Date: Thu, 12 Feb 2026 23:57:58 +0300 Subject: [PATCH] feat: update docs, fix repository, full working github callback, fix healthcheck in docker file, update makefile --- backend/Dockerfile | 1 + backend/Makefile | 11 +++- backend/docs/docs.go | 50 ++++++++++++++++--- backend/docs/swagger.json | 50 ++++++++++++++++--- backend/docs/swagger.yaml | 36 ++++++++++--- backend/internal/handlers/auth_handlers.go | 15 +++++- .../internal/repositories/auth_repository.go | 44 +++++++++++++--- backend/internal/repositories/interface.go | 1 + backend/internal/storage/models.go | 8 +-- 9 files changed, 182 insertions(+), 34 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 8f70254..fc66c3b 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -15,5 +15,6 @@ FROM alpine:3.23.0 COPY --from=builder /app/backend . EXPOSE 8080 +RUN apk add --no-cache curl HEALTHCHECK CMD curl --fail http://localhost:8080/health || exit 1 CMD ["./backend"] diff --git a/backend/Makefile b/backend/Makefile index bd98eea..190c417 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -1,4 +1,4 @@ -.PHONY: test build clean lint dev +.PHONY: test build clean lint dev run-docker docs-upd test: @@ -19,3 +19,12 @@ lint: dev: swag init -g ./cmd/main.go --parseDependency --parseInternal go run ./cmd/main.go + + +docs-upd: + swag init -g ./cmd/main.go --parseDependency --parseInternal + + +run-docker: + docker build -t backend . + docker run --rm -p 8080:8080 --env-file .env backend:latest diff --git a/backend/docs/docs.go b/backend/docs/docs.go index fb0a6ec..853484d 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -15,22 +15,58 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/auth/github": { + "get": { + "description": "Redirects to GitHub authorization", + "tags": [ + "auth" + ], + "summary": "Start GitHub OAuth login", + "responses": { + "302": { + "description": "Found" + } + } + } + }, "/callback/github": { "get": { - "description": "Callback for oauth2 providers", - "consumes": [ - "application/json" - ], + "description": "Exchanges authorization code for access token", "produces": [ "application/json" ], "tags": [ "auth" ], - "summary": "Callback for oauth2 providers", + "summary": "GitHub OAuth callback", + "parameters": [ + { + "type": "string", + "description": "Authorization code", + "name": "code", + "in": "query", + "required": true + } + ], "responses": { "200": { - "description": "OK", + "description": "Access token", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Missing code", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Exchange failed", "schema": { "type": "object", "additionalProperties": { @@ -227,7 +263,7 @@ const docTemplate = `{ "content": { "type": "string" }, - "createdAt": { + "created_at": { "type": "string" }, "id": { diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index c52c036..bccf84d 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -4,22 +4,58 @@ "contact": {} }, "paths": { + "/auth/github": { + "get": { + "description": "Redirects to GitHub authorization", + "tags": [ + "auth" + ], + "summary": "Start GitHub OAuth login", + "responses": { + "302": { + "description": "Found" + } + } + } + }, "/callback/github": { "get": { - "description": "Callback for oauth2 providers", - "consumes": [ - "application/json" - ], + "description": "Exchanges authorization code for access token", "produces": [ "application/json" ], "tags": [ "auth" ], - "summary": "Callback for oauth2 providers", + "summary": "GitHub OAuth callback", + "parameters": [ + { + "type": "string", + "description": "Authorization code", + "name": "code", + "in": "query", + "required": true + } + ], "responses": { "200": { - "description": "OK", + "description": "Access token", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Missing code", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "500": { + "description": "Exchange failed", "schema": { "type": "object", "additionalProperties": { @@ -216,7 +252,7 @@ "content": { "type": "string" }, - "createdAt": { + "created_at": { "type": "string" }, "id": { diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 3a1cd46..bd83ce3 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -6,7 +6,7 @@ definitions: properties: content: type: string - createdAt: + created_at: type: string id: type: integer @@ -32,21 +32,45 @@ definitions: info: contact: {} paths: + /auth/github: + get: + description: Redirects to GitHub authorization + responses: + "302": + description: Found + summary: Start GitHub OAuth login + tags: + - auth /callback/github: get: - consumes: - - application/json - description: Callback for oauth2 providers + description: Exchanges authorization code for access token + parameters: + - description: Authorization code + in: query + name: code + required: true + type: string produces: - application/json responses: "200": - description: OK + description: Access token + schema: + additionalProperties: true + type: object + "400": + description: Missing code schema: additionalProperties: type: string type: object - summary: Callback for oauth2 providers + "500": + description: Exchange failed + schema: + additionalProperties: + type: string + type: object + summary: GitHub OAuth callback tags: - auth /posts: diff --git a/backend/internal/handlers/auth_handlers.go b/backend/internal/handlers/auth_handlers.go index c404a39..c13faa7 100644 --- a/backend/internal/handlers/auth_handlers.go +++ b/backend/internal/handlers/auth_handlers.go @@ -49,7 +49,7 @@ func NewAuthHandlers(repo repositories.AuthRepository) *AuthHandlers { // @Description Redirects to GitHub authorization // @Tags auth // @Success 302 -// @Router /api/v1/auth/github [get] +// @Router /auth/github [get] func (h *AuthHandlers) LoginGithub(c *gin.Context) { url := h.config.AuthCodeURL("state", oauth2.AccessTypeOnline) h.logger.Info("Redirect to GitHub: " + url) @@ -117,7 +117,20 @@ func (h *AuthHandlers) CallbackGithub(c *gin.Context) { c.JSON(500, gin.H{"error": "registration failed", "details": err.Error()}) return } + } else { + h.logger.Info("Existing user, fetching data: " + ghUser.GithubLogin) + user, err := h.repo.GetUserByGithubID(c.Request.Context(), ghUser.GithubID) + if err != nil { + h.logger.Error("Failed to fetch user: " + err.Error()) + c.JSON(500, gin.H{"error": "failed to fetch user", "details": err.Error()}) + return + } + id = user.ID + ghUser.GithubLogin = user.GithubLogin + ghUser.Email = user.Email + ghUser.AvatarURL = user.AvatarURL } + user := storage.User{ ID: id, GithubID: ghUser.GithubID, diff --git a/backend/internal/repositories/auth_repository.go b/backend/internal/repositories/auth_repository.go index 5361516..15d163c 100644 --- a/backend/internal/repositories/auth_repository.go +++ b/backend/internal/repositories/auth_repository.go @@ -22,28 +22,56 @@ func NewAuthRepository(db *sql.DB) AuthRepository { func (a *authRepository) Register(ctx context.Context, user storage.UserReg) (int, error) { var id int - _, err := a.db.Exec( + _, err := a.db.ExecContext(ctx, "INSERT INTO users(email, github_id, github_login, avatar_url) VALUES(?, ?, ?, ?)", + user.Email, user.GithubID, user.GithubLogin, user.AvatarURL, ) if err != nil { - a.logger.Error("error request: " + err.Error()) + a.logger.Error("error insert: " + err.Error()) return 0, err } - row := a.db.QueryRow("SELECT id FROM users WHERE github_id = ?", user.GithubID) + + row := a.db.QueryRowContext(ctx, "SELECT id FROM users WHERE github_id = ?", user.GithubID) err = row.Scan(&id) if err != nil { a.logger.Error("error scan: " + err.Error()) return 0, err } - a.logger.Info("User registered:", "email", user.Email) + a.logger.Info("User registered: " + user.Email) return id, nil } func (a *authRepository) IsRegistered(ctx context.Context, github_id int) (bool, error) { - row := a.db.QueryRow("SELECT id FROM users WHERE github_id = ?", github_id) - if row != nil { - return true, nil + var id int + err := a.db.QueryRowContext(ctx, "SELECT id FROM users WHERE github_id = ?", github_id). + Scan(&id) + if err != nil { + if err == sql.ErrNoRows { + return false, nil + } + return false, err } - return false, nil + return true, nil +} + +func (r *authRepository) GetUserByGithubID( + ctx context.Context, + githubID int, +) (*storage.User, error) { + var user storage.User + query := `SELECT id, github_id, github_login, email, avatar_url FROM users WHERE github_id = ?` + + err := r.db.QueryRowContext(ctx, query, githubID).Scan( + &user.ID, + &user.GithubID, + &user.GithubLogin, + &user.Email, + &user.AvatarURL, + ) + if err != nil { + return nil, err + } + + return &user, nil } diff --git a/backend/internal/repositories/interface.go b/backend/internal/repositories/interface.go index b49bdcf..33819c2 100644 --- a/backend/internal/repositories/interface.go +++ b/backend/internal/repositories/interface.go @@ -17,4 +17,5 @@ type PostRepository interface { type AuthRepository interface { Register(ctx context.Context, user storage.UserReg) (int, error) IsRegistered(ctx context.Context, github_id int) (bool, error) + GetUserByGithubID(ctx context.Context, githubID int) (*storage.User, error) } diff --git a/backend/internal/storage/models.go b/backend/internal/storage/models.go index c9503b4..ea70986 100644 --- a/backend/internal/storage/models.go +++ b/backend/internal/storage/models.go @@ -27,8 +27,8 @@ type User struct { } type UserReg struct { - Email string `db:"email" json:"email"` - GithubID int `db:"github_id" json:"github_id"` - GithubLogin string `db:"github_login" json:"github_login"` - AvatarURL string `db:"avatar_url" json:"avatar_url"` + Email string `json:"email"` + GithubID int `json:"id"` + GithubLogin string `json:"login"` + AvatarURL string `json:"avatar_url"` }