Compare commits

...

2 Commits

Author SHA1 Message Date
d3m0k1d
15cf6cca8a del fake db
All checks were successful
Backend ci / build (push) Successful in 3m44s
2026-02-12 23:58:56 +03:00
d3m0k1d
d9cf0ade7f feat: update docs, fix repository, full working github callback, fix healthcheck in docker file, update makefile 2026-02-12 23:58:56 +03:00
10 changed files with 182 additions and 34 deletions

View File

@@ -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"]

View File

@@ -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

View File

@@ -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": {

View File

@@ -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": {

View File

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

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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"`
}