add bb_api rest api for begushiybashkir

This commit is contained in:
2025-10-07 05:23:40 +05:00
parent de0c992db9
commit 23d68e53ab
16 changed files with 552 additions and 1 deletions
+27
View File
@@ -0,0 +1,27 @@
package main
import (
"log"
"net/http"
"go-rest-api/internal/config"
"go-rest-api/internal/routes"
"go-rest-api/pkg/database"
)
func main() {
// Load configuration
cfg := config.Load()
// Initialize database
db, err := database.InitDB(cfg.DatabaseURL)
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
// Initialize router
router := routes.SetupRouter(db)
log.Printf("Server starting on port %s", cfg.Port)
log.Fatal(http.ListenAndServe(":"+cfg.Port, router))
}
+16
View File
@@ -0,0 +1,16 @@
module go-rest-api
go 1.21
require (
github.com/go-chi/chi/v5 v5.0.10
github.com/go-chi/cors v1.2.1
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.5
)
require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
)
+14
View File
@@ -0,0 +1,14 @@
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
@@ -0,0 +1,26 @@
package config
import "os"
type Config struct {
Port string
DatabaseURL string
}
func Load() *Config {
port := getEnv("PORT", "8080")
databaseURL := getEnv("DATABASE_URL", "sqlite:test.db")
return &Config{
Port: port,
DatabaseURL: databaseURL,
}
}
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
@@ -0,0 +1,97 @@
package handlers
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"go-rest-api/internal/models"
"go-rest-api/internal/service"
)
type AuthHandler struct {
authService service.AuthService
}
func NewAuthHandler(authService service.AuthService) *AuthHandler {
return &AuthHandler{authService: authService}
}
func (h *AuthHandler) Routes() chi.Router {
r := chi.NewRouter()
r.Post("/register", h.Register)
r.Post("/login", h.Login)
r.Get("/check", h.AuthCheck)
return r
}
type RegisterRequest struct {
Email string `json:"email"`
Password string `json:"password"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) {
var req RegisterRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
user := &models.User{
Email: req.Email,
Password: req.Password,
FirstName: req.FirstName,
LastName: req.LastName,
Role: "user",
}
if err := h.authService.Register(user); err != nil {
respondWithError(w, http.StatusBadRequest, err.Error())
return
}
respondWithJSON(w, http.StatusCreated, map[string]string{
"message": "User registered successfully",
})
}
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
var req LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
user, err := h.authService.Login(req.Email, req.Password)
if err != nil {
respondWithError(w, http.StatusUnauthorized, err.Error())
return
}
respondWithJSON(w, http.StatusOK, map[string]interface{}{
"message": "Login successful",
"user": map[string]interface{}{
"id": user.ID,
"email": user.Email,
"first_name": user.FirstName,
"last_name": user.LastName,
"role": user.Role,
},
})
}
func (h *AuthHandler) AuthCheck(w http.ResponseWriter, r *http.Request) {
respondWithJSON(w, http.StatusOK, map[string]string{
"status": "ok",
"message": "Auth endpoint is working",
})
}
@@ -0,0 +1,16 @@
package handlers
import (
"encoding/json"
"net/http"
)
func respondWithJSON(w http.ResponseWriter, status int, payload interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(payload)
}
func respondWithError(w http.ResponseWriter, status int, message string) {
respondWithJSON(w, status, map[string]string{"error": message})
}
@@ -0,0 +1,22 @@
package handlers
import (
"go-rest-api/internal/repository"
"go-rest-api/internal/service"
"gorm.io/gorm"
)
type Handler struct {
healthHandler *HealthHandler
authHandler *AuthHandler
}
func NewHandler(db *gorm.DB) *Handler {
userRepo := repository.NewUserRepository(db)
authService := service.NewAuthService(userRepo)
return &Handler{
healthHandler: NewHealthHandler(),
authHandler: NewAuthHandler(authService),
}
}
@@ -0,0 +1,40 @@
package handlers
import (
"net/http"
"github.com/go-chi/chi/v5"
)
type HealthHandler struct{}
func NewHealthHandler() *HealthHandler {
return &HealthHandler{}
}
func (h *HealthHandler) Routes() chi.Router {
r := chi.NewRouter()
r.Get("/health", h.HealthCheck)
r.Get("/check", h.Check)
return r
}
func (h *HealthHandler) HealthCheck(w http.ResponseWriter, r *http.Request) {
response := map[string]string{
"status": "ok",
"message": "Service is healthy",
}
respondWithJSON(w, http.StatusOK, response)
}
func (h *HealthHandler) Check(w http.ResponseWriter, r *http.Request) {
response := map[string]string{
"status": "ok",
"message": "API is working",
}
respondWithJSON(w, http.StatusOK, response)
}
+23
View File
@@ -0,0 +1,23 @@
package models
import (
"time"
"gorm.io/gorm"
)
type User struct {
gorm.Model
ID uint `gorm:"primaryKey" json:"id"`
Email string `gorm:"uniqueIndex;not null" json:"email"`
Password string `gorm:"not null" json:"-"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Role string `gorm:"default:user" json:"role"` // user, admin
IsActive bool `gorm:"default:true" json:"is_active"`
LastLogin time.Time `json:"last_login"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// Другие модели будут добавлены позже
// type Profile {}, type Event {}, type Review {}, etc.
@@ -0,0 +1,46 @@
package repository
import (
"go-rest-api/internal/models"
"gorm.io/gorm"
)
type UserRepository interface {
Create(user *models.User) error
FindByID(id uint) (*models.User, error)
FindByEmail(email string) (*models.User, error)
Update(user *models.User) error
Delete(id uint) error
}
type userRepository struct {
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{db: db}
}
func (r *userRepository) Create(user *models.User) error {
return r.db.Create(user).Error
}
func (r *userRepository) FindByID(id uint) (*models.User, error) {
var user models.User
err := r.db.First(&user, id).Error
return &user, err
}
func (r *userRepository) FindByEmail(email string) (*models.User, error) {
var user models.User
err := r.db.Where("email = ?", email).First(&user).Error
return &user, err
}
func (r *userRepository) Update(user *models.User) error {
return r.db.Save(user).Error
}
func (r *userRepository) Delete(id uint) error {
return r.db.Delete(&models.User{}, id).Error
}
@@ -0,0 +1,47 @@
package routes
import (
"net/http"
"github.com/go-chi/chi/v5"
"gorm.io/gorm"
"go-rest-api/internal/handlers"
"go-rest-api/internal/repository"
"go-rest-api/internal/service"
"go-rest-api/pkg/middleware"
)
func SetupRouter(db *gorm.DB) http.Handler {
r := chi.NewRouter()
// Apply common middleware
for _, m := range middleware.CommonMiddleware() {
r.Use(m)
}
// Initialize repositories
userRepo := repository.NewUserRepository(db)
// Initialize services
authService := service.NewAuthService(userRepo)
// Initialize handlers
healthHandler := handlers.NewHealthHandler()
authHandler := handlers.NewAuthHandler(authService)
// Health routes
r.Mount("/api", healthHandler.Routes())
// API v1 routes
r.Route("/api/v1", func(r chi.Router) {
r.Mount("/auth", authHandler.Routes())
// Здесь будут добавлены другие маршруты:
// r.Mount("/users", userHandler.Routes())
// r.Mount("/events", eventHandler.Routes())
// r.Mount("/reviews", reviewHandler.Routes())
})
return r
}
@@ -0,0 +1,52 @@
package service
import (
"errors"
"go-rest-api/internal/models"
"go-rest-api/internal/repository"
)
type AuthService interface {
Register(user *models.User) error
Login(email, password string) (*models.User, error)
}
type authService struct {
userRepo repository.UserRepository
}
func NewAuthService(userRepo repository.UserRepository) AuthService {
return &authService{userRepo: userRepo}
}
func (s *authService) Register(user *models.User) error {
// Проверка существования пользователя
existingUser, _ := s.userRepo.FindByEmail(user.Email)
if existingUser != nil {
return errors.New("user already exists")
}
// Здесь должна быть хеширование пароля
// user.Password = hashPassword(user.Password)
return s.userRepo.Create(user)
}
func (s *authService) Login(email, password string) (*models.User, error) {
user, err := s.userRepo.FindByEmail(email)
if err != nil {
return nil, errors.New("invalid credentials")
}
// Здесь должна быть проверка хеша пароля
// if !checkPasswordHash(password, user.Password) {
// return nil, errors.New("invalid credentials")
// }
// Временно просто проверяем напрямую (для демо)
if user.Password != password {
return nil, errors.New("invalid credentials")
}
return user, nil
}
@@ -0,0 +1,25 @@
package database
import (
"go-rest-api/internal/models"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func InitDB(dsn string) (*gorm.DB, error) {
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
// Auto migrate models
err = db.AutoMigrate(
&models.User{},
// Добавьте другие модели здесь по мере расширения
)
if err != nil {
return nil, err
}
return db, nil
}
@@ -0,0 +1,24 @@
package middleware
import (
"net/http"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
)
func CommonMiddleware() []func(http.Handler) http.Handler {
return []func(http.Handler) http.Handler{
middleware.Logger,
middleware.Recoverer,
middleware.RequestID,
cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: false,
MaxAge: 300,
}),
}
}
+26
View File
@@ -0,0 +1,26 @@
go-rest-api/
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── config/
│ │ └── config.go
│ ├── handlers/
│ │ ├── health.go
│ │ ├── auth.go
│ │ └── handlers.go
│ ├── models/
│ │ └── user.go
│ ├── repository/
│ │ └── user_repository.go
│ ├── service/
│ │ └── auth_service.go
│ └── routes/
│ └── routes.go
├── pkg/
│ ├── database/
│ │ └── database.go
│ └── middleware/
│ └── middleware.go
├── go.mod
└── go.sum
+51 -1
View File
@@ -81,17 +81,64 @@ services:
- postgres_data:/var/lib/postgresql/data
- ./migrations:/docker-entrypoint-initdb.d
networks:
- app-network
- bb-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 10s
retries: 5
api_db:
build:
context: ./api_bb
dockerfile: Dockerfile
ports:
- "7777:8080"
container_name: api_bb
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
# Database connection settings
DB_HOST: db
DB_PORT: 5432
DB_USER: postgres
DB_PASSWORD: postgres
DB_NAME: mydb
APP_PORT: 8080
networks:
- bb-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
bb_db:
image: postgres:15-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mydb
volumes:
- bb_data:/var/lib/postgresql/data
networks:
- bb_network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 10s
retries: 5
volumes:
certbot_data:
certbot_www:
postgres_data:
bb_data;
networks:
web-network:
@@ -100,3 +147,6 @@ networks:
driver: bridge
app-network:
name: serv_golang_rest_api_app-network
bb_network:
name: begushiy_bashkir_api_network