feat: update docs, fix repository, full working github callback, fix healthcheck in docker file, update makefile
This commit is contained in:
@@ -15,5 +15,6 @@ FROM alpine:3.23.0
|
|||||||
COPY --from=builder /app/backend .
|
COPY --from=builder /app/backend .
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
RUN apk add --no-cache curl
|
||||||
HEALTHCHECK CMD curl --fail http://localhost:8080/health || exit 1
|
HEALTHCHECK CMD curl --fail http://localhost:8080/health || exit 1
|
||||||
CMD ["./backend"]
|
CMD ["./backend"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: test build clean lint dev
|
.PHONY: test build clean lint dev run-docker docs-upd
|
||||||
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@@ -19,3 +19,12 @@ lint:
|
|||||||
dev:
|
dev:
|
||||||
swag init -g ./cmd/main.go --parseDependency --parseInternal
|
swag init -g ./cmd/main.go --parseDependency --parseInternal
|
||||||
go run ./cmd/main.go
|
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
|
||||||
|
|||||||
@@ -15,22 +15,58 @@ const docTemplate = `{
|
|||||||
"host": "{{.Host}}",
|
"host": "{{.Host}}",
|
||||||
"basePath": "{{.BasePath}}",
|
"basePath": "{{.BasePath}}",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"/auth/github": {
|
||||||
|
"get": {
|
||||||
|
"description": "Redirects to GitHub authorization",
|
||||||
|
"tags": [
|
||||||
|
"auth"
|
||||||
|
],
|
||||||
|
"summary": "Start GitHub OAuth login",
|
||||||
|
"responses": {
|
||||||
|
"302": {
|
||||||
|
"description": "Found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/callback/github": {
|
"/callback/github": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Callback for oauth2 providers",
|
"description": "Exchanges authorization code for access token",
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"auth"
|
"auth"
|
||||||
],
|
],
|
||||||
"summary": "Callback for oauth2 providers",
|
"summary": "GitHub OAuth callback",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Authorization code",
|
||||||
|
"name": "code",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"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": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
@@ -227,7 +263,7 @@ const docTemplate = `{
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"createdAt": {
|
"created_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
|
|||||||
@@ -4,22 +4,58 @@
|
|||||||
"contact": {}
|
"contact": {}
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"/auth/github": {
|
||||||
|
"get": {
|
||||||
|
"description": "Redirects to GitHub authorization",
|
||||||
|
"tags": [
|
||||||
|
"auth"
|
||||||
|
],
|
||||||
|
"summary": "Start GitHub OAuth login",
|
||||||
|
"responses": {
|
||||||
|
"302": {
|
||||||
|
"description": "Found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/callback/github": {
|
"/callback/github": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Callback for oauth2 providers",
|
"description": "Exchanges authorization code for access token",
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"auth"
|
"auth"
|
||||||
],
|
],
|
||||||
"summary": "Callback for oauth2 providers",
|
"summary": "GitHub OAuth callback",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Authorization code",
|
||||||
|
"name": "code",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"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": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
@@ -216,7 +252,7 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"createdAt": {
|
"created_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
content:
|
content:
|
||||||
type: string
|
type: string
|
||||||
createdAt:
|
created_at:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
@@ -32,21 +32,45 @@ definitions:
|
|||||||
info:
|
info:
|
||||||
contact: {}
|
contact: {}
|
||||||
paths:
|
paths:
|
||||||
|
/auth/github:
|
||||||
|
get:
|
||||||
|
description: Redirects to GitHub authorization
|
||||||
|
responses:
|
||||||
|
"302":
|
||||||
|
description: Found
|
||||||
|
summary: Start GitHub OAuth login
|
||||||
|
tags:
|
||||||
|
- auth
|
||||||
/callback/github:
|
/callback/github:
|
||||||
get:
|
get:
|
||||||
consumes:
|
description: Exchanges authorization code for access token
|
||||||
- application/json
|
parameters:
|
||||||
description: Callback for oauth2 providers
|
- description: Authorization code
|
||||||
|
in: query
|
||||||
|
name: code
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: Access token
|
||||||
|
schema:
|
||||||
|
additionalProperties: true
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Missing code
|
||||||
schema:
|
schema:
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
summary: Callback for oauth2 providers
|
"500":
|
||||||
|
description: Exchange failed
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
summary: GitHub OAuth callback
|
||||||
tags:
|
tags:
|
||||||
- auth
|
- auth
|
||||||
/posts:
|
/posts:
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func NewAuthHandlers(repo repositories.AuthRepository) *AuthHandlers {
|
|||||||
// @Description Redirects to GitHub authorization
|
// @Description Redirects to GitHub authorization
|
||||||
// @Tags auth
|
// @Tags auth
|
||||||
// @Success 302
|
// @Success 302
|
||||||
// @Router /api/v1/auth/github [get]
|
// @Router /auth/github [get]
|
||||||
func (h *AuthHandlers) LoginGithub(c *gin.Context) {
|
func (h *AuthHandlers) LoginGithub(c *gin.Context) {
|
||||||
url := h.config.AuthCodeURL("state", oauth2.AccessTypeOnline)
|
url := h.config.AuthCodeURL("state", oauth2.AccessTypeOnline)
|
||||||
h.logger.Info("Redirect to GitHub: " + url)
|
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()})
|
c.JSON(500, gin.H{"error": "registration failed", "details": err.Error()})
|
||||||
return
|
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{
|
user := storage.User{
|
||||||
ID: id,
|
ID: id,
|
||||||
GithubID: ghUser.GithubID,
|
GithubID: ghUser.GithubID,
|
||||||
|
|||||||
@@ -22,28 +22,56 @@ func NewAuthRepository(db *sql.DB) AuthRepository {
|
|||||||
|
|
||||||
func (a *authRepository) Register(ctx context.Context, user storage.UserReg) (int, error) {
|
func (a *authRepository) Register(ctx context.Context, user storage.UserReg) (int, error) {
|
||||||
var id int
|
var id int
|
||||||
_, err := a.db.Exec(
|
_, err := a.db.ExecContext(ctx,
|
||||||
"INSERT INTO users(email, github_id, github_login, avatar_url) VALUES(?, ?, ?, ?)",
|
"INSERT INTO users(email, github_id, github_login, avatar_url) VALUES(?, ?, ?, ?)",
|
||||||
|
user.Email, user.GithubID, user.GithubLogin, user.AvatarURL,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Error("error request: " + err.Error())
|
a.logger.Error("error insert: " + err.Error())
|
||||||
return 0, err
|
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)
|
err = row.Scan(&id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Error("error scan: " + err.Error())
|
a.logger.Error("error scan: " + err.Error())
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
a.logger.Info("User registered:", "email", user.Email)
|
a.logger.Info("User registered: " + user.Email)
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *authRepository) IsRegistered(ctx context.Context, github_id int) (bool, error) {
|
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)
|
var id int
|
||||||
if row != nil {
|
err := a.db.QueryRowContext(ctx, "SELECT id FROM users WHERE github_id = ?", github_id).
|
||||||
return true, nil
|
Scan(&id)
|
||||||
}
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,4 +17,5 @@ type PostRepository interface {
|
|||||||
type AuthRepository interface {
|
type AuthRepository interface {
|
||||||
Register(ctx context.Context, user storage.UserReg) (int, error)
|
Register(ctx context.Context, user storage.UserReg) (int, error)
|
||||||
IsRegistered(ctx context.Context, github_id int) (bool, error)
|
IsRegistered(ctx context.Context, github_id int) (bool, error)
|
||||||
|
GetUserByGithubID(ctx context.Context, githubID int) (*storage.User, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ type User struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserReg struct {
|
type UserReg struct {
|
||||||
Email string `db:"email" json:"email"`
|
Email string `json:"email"`
|
||||||
GithubID int `db:"github_id" json:"github_id"`
|
GithubID int `json:"id"`
|
||||||
GithubLogin string `db:"github_login" json:"github_login"`
|
GithubLogin string `json:"login"`
|
||||||
AvatarURL string `db:"avatar_url" json:"avatar_url"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user