new file: .env
modified: Dockerfile new file: cmd/api/main.go modified: docker-compose.yaml modified: go.mod modified: go.sum new file: internal/config/config.go new file: internal/handler/middleware.go new file: internal/handler/user_handler.go new file: internal/model/user.go new file: internal/repository/user_repository.go new file: internal/server/server.go new file: internal/service/user_service.go new file: main deleted: main.go new file: migrations/001_create_users.sql new file: pkg/database/postgres.go deleted: src/models/user.go add files from deepseek for rest api server on golang gorm and chi server router
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# DB environment variabels
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_USER=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_NAME=mydb
|
||||
APP_PORT=8080
|
||||
@@ -1,14 +1,33 @@
|
||||
# Build stage
|
||||
FROM golang:1.25.1-alpine as builder
|
||||
# Билд стадия
|
||||
FROM golang:1.25.1-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем зависимости
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY *.go ./
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /go/bin/app
|
||||
|
||||
# Final stage
|
||||
FROM gcr.io/distroless/static-debian12
|
||||
WORKDIR /
|
||||
COPY --from=builder /go/bin/app /app
|
||||
CMD ["/app"]
|
||||
EXPOSE 8080
|
||||
# Копируем исходный код
|
||||
COPY . .
|
||||
|
||||
# Собираем приложение
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/api
|
||||
|
||||
# Финальная стадия
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk --no-cache add ca-certificates
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# Копируем бинарник из builder стадии
|
||||
COPY --from=builder /app/main .
|
||||
|
||||
# Копируем миграции
|
||||
COPY --from=builder /app/migrations ./migrations
|
||||
|
||||
# Экспозим порт
|
||||
EXPOSE 8080
|
||||
|
||||
# Запускаем приложение
|
||||
CMD ["./main"]
|
||||
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"serv_golang_rest_api/internal/config"
|
||||
"serv_golang_rest_api/internal/server"
|
||||
"serv_golang_rest_api/pkg/database"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Загрузка конфигурации
|
||||
cfg := config.Load()
|
||||
|
||||
// Подключение к БД
|
||||
db, err := database.NewPostgresConnection(cfg)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
|
||||
// Создание и запуск сервера
|
||||
srv := server.New(db)
|
||||
|
||||
log.Printf("Server starting on port %s", cfg.AppPort)
|
||||
if err := srv.Run(cfg.AppPort); err != nil {
|
||||
log.Fatal("Failed to start server:", err)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
services:
|
||||
go-server:
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
@@ -8,7 +8,8 @@ services:
|
||||
container_name: serv_golang_rest_api
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# Database connection settings
|
||||
DB_HOST: db
|
||||
@@ -16,8 +17,14 @@ services:
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_NAME: mydb
|
||||
APP_PORT: 8080
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
@@ -29,11 +36,18 @@ services:
|
||||
POSTGRES_DB: mydb
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./migrations:/docker-entrypoint-initdb.d
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
||||
networks:
|
||||
app-network:
|
||||
app-network:
|
||||
driver: bridge
|
||||
@@ -1,6 +1,6 @@
|
||||
module serv_golang_rest_api
|
||||
|
||||
go 1.22.5
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
@@ -8,13 +8,16 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.2.3
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
||||
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package config
|
||||
|
||||
import "os"
|
||||
|
||||
type Config struct {
|
||||
DBHost string
|
||||
DBPort string
|
||||
DBUser string
|
||||
DBPassword string
|
||||
DBName string
|
||||
AppPort string
|
||||
}
|
||||
|
||||
func Load() *Config {
|
||||
return &Config{
|
||||
DBHost: getEnv("DB_HOST", "localhost"),
|
||||
DBPort: getEnv("DB_PORT", "5432"),
|
||||
DBUser: getEnv("DB_USER", "postgres"),
|
||||
DBPassword: getEnv("DB_PASSWORD", "postgres"),
|
||||
DBName: getEnv("DB_NAME", "mydb"),
|
||||
AppPort: getEnv("APP_PORT", "8080"),
|
||||
}
|
||||
}
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"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.Timeout(60 * time.Second),
|
||||
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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"serv_golang_rest_api/internal/model"
|
||||
"serv_golang_rest_api/internal/service"
|
||||
)
|
||||
|
||||
type UserHandler struct {
|
||||
userService *service.UserService
|
||||
}
|
||||
|
||||
func NewUserHandler(userService *service.UserService) *UserHandler {
|
||||
return &UserHandler{userService: userService}
|
||||
}
|
||||
|
||||
func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
var req model.CreateUserRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.CreateUser(&req)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
|
||||
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid user ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.GetUserByID(uint(id))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
|
||||
func (h *UserHandler) GetAllUsers(w http.ResponseWriter, r *http.Request) {
|
||||
users, err := h.userService.GetAllUsers()
|
||||
if err != nil {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(users)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"index"`
|
||||
|
||||
Name string `json:"name" gorm:"size:100;not null"`
|
||||
Email string `json:"email" gorm:"size:255;uniqueIndex;not null"`
|
||||
Password string `json:"-" gorm:"size:255;not null"` // Пароль не возвращаем в JSON
|
||||
}
|
||||
|
||||
type CreateUserRequest struct {
|
||||
Name string `json:"name" validate:"required,min=2,max=100"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=6"`
|
||||
}
|
||||
|
||||
type UpdateUserRequest struct {
|
||||
Name string `json:"name" validate:"omitempty,min=2,max=100"`
|
||||
Email string `json:"email" validate:"omitempty,email"`
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"serv_golang_rest_api/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewUserRepository(db *gorm.DB) *UserRepository {
|
||||
return &UserRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *UserRepository) Create(user *model.User) error {
|
||||
return r.db.Create(user).Error
|
||||
}
|
||||
|
||||
func (r *UserRepository) FindByID(id uint) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.db.First(&user, id).Error
|
||||
return &user, err
|
||||
}
|
||||
|
||||
func (r *UserRepository) FindByEmail(email string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.db.Where("email = ?", email).First(&user).Error
|
||||
return &user, err
|
||||
}
|
||||
|
||||
func (r *UserRepository) FindAll() ([]model.User, error) {
|
||||
var users []model.User
|
||||
err := r.db.Find(&users).Error
|
||||
return users, err
|
||||
}
|
||||
|
||||
func (r *UserRepository) Update(user *model.User) error {
|
||||
return r.db.Save(user).Error
|
||||
}
|
||||
|
||||
func (r *UserRepository) Delete(id uint) error {
|
||||
return r.db.Delete(&model.User{}, id).Error
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"serv_golang_rest_api/internal/handler"
|
||||
"serv_golang_rest_api/internal/repository"
|
||||
"serv_golang_rest_api/internal/service"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
router *chi.Mux
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func New(db *gorm.DB) *Server {
|
||||
s := &Server{
|
||||
router: chi.NewRouter(),
|
||||
db: db,
|
||||
}
|
||||
s.configureRouter()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) configureRouter() {
|
||||
// Общие middleware
|
||||
for _, middleware := range handler.CommonMiddleware() {
|
||||
s.router.Use(middleware)
|
||||
}
|
||||
|
||||
// Health check
|
||||
s.router.Get("/health", s.healthCheck)
|
||||
|
||||
// API routes
|
||||
s.router.Route("/api/v1", func(r chi.Router) {
|
||||
s.setupUserRoutes(r)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) setupUserRoutes(r chi.Router) {
|
||||
userRepo := repository.NewUserRepository(s.db)
|
||||
userService := service.NewUserService(userRepo)
|
||||
userHandler := handler.NewUserHandler(userService)
|
||||
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.Get("/", userHandler.GetAllUsers)
|
||||
r.Post("/", userHandler.CreateUser)
|
||||
r.Get("/{id}", userHandler.GetUser)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) healthCheck(w http.ResponseWriter, r *http.Request) {
|
||||
// Проверяем соединение с БД
|
||||
sqlDB, err := s.db.DB()
|
||||
if err != nil {
|
||||
http.Error(w, "Database connection error", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
if err := sqlDB.Ping(); err != nil {
|
||||
http.Error(w, "Database ping failed", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"status": "healthy",
|
||||
"timestamp": http.TimeFormat,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) Run(port string) error {
|
||||
return http.ListenAndServe(":"+port, s.router)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"serv_golang_rest_api/internal/model"
|
||||
"serv_golang_rest_api/internal/repository"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
userRepo *repository.UserRepository
|
||||
}
|
||||
|
||||
func NewUserService(userRepo *repository.UserRepository) *UserService {
|
||||
return &UserService{userRepo: userRepo}
|
||||
}
|
||||
|
||||
func (s *UserService) CreateUser(req *model.CreateUserRequest) (*model.UserResponse, error) {
|
||||
// Проверяем существует ли пользователь с таким email
|
||||
existingUser, _ := s.userRepo.FindByEmail(req.Email)
|
||||
if existingUser != nil {
|
||||
return nil, errors.New("user with this email already exists")
|
||||
}
|
||||
|
||||
// Хешируем пароль
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := &model.User{
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
Password: string(hashedPassword),
|
||||
}
|
||||
|
||||
if err := s.userRepo.Create(user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.toUserResponse(user), nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserByID(id uint) (*model.UserResponse, error) {
|
||||
user, err := s.userRepo.FindByID(id)
|
||||
if err != nil {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
|
||||
return s.toUserResponse(user), nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetAllUsers() ([]model.UserResponse, error) {
|
||||
users, err := s.userRepo.FindAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var responses []model.UserResponse
|
||||
for _, user := range users {
|
||||
responses = append(responses, *s.toUserResponse(&user))
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (s *UserService) toUserResponse(user *model.User) *model.UserResponse {
|
||||
return &model.UserResponse{
|
||||
ID: user.ID,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,83 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Example model
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
func getUser(w http.ResponseWriter, r *http.Request) {
|
||||
db, ok := r.Context().Value("db").(*gorm.DB)
|
||||
if !ok {
|
||||
http.Error(w, "Database connection not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := db.First(&user).Error; err != nil {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
// Get DB connection from context
|
||||
db, ok := r.Context().Value("db").(*gorm.DB)
|
||||
if !ok {
|
||||
http.Error(w, "Database connection not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Example usage
|
||||
db.AutoMigrate(&User{})
|
||||
user := User{Name: "Test", Email: "test@example.com"}
|
||||
db.Create(&user)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
response := Response{Message: "ok"}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create DB connection string
|
||||
dsn := "host=db user=postgres password=postgres dbname=mydb port=5432 sslmode=disable TimeZone=UTC"
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to database:", err)
|
||||
}
|
||||
|
||||
// Initialize database
|
||||
db.AutoMigrate(&User{})
|
||||
|
||||
// Setup HTTP server with DB context
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), "db", db))
|
||||
handler(w, r)
|
||||
})
|
||||
|
||||
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), "db", db))
|
||||
getUser(w, r)
|
||||
})
|
||||
|
||||
log.Println("Сервер запущен на http://localhost:8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Таблица пользователей
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE NULL
|
||||
);
|
||||
|
||||
-- Индекс для быстрого поиска по email
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
|
||||
-- Индекс для мягкого удаления
|
||||
CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON users(deleted_at);
|
||||
@@ -0,0 +1,45 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"serv_golang_rest_api/internal/config"
|
||||
"serv_golang_rest_api/internal/model"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func NewPostgresConnection(cfg *config.Config) (*gorm.DB, error) {
|
||||
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=UTC",
|
||||
cfg.DBHost, cfg.DBUser, cfg.DBPassword, cfg.DBName, cfg.DBPort)
|
||||
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to database: %w", err)
|
||||
}
|
||||
|
||||
// Автомиграция
|
||||
if err := autoMigrate(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Println("Successfully connected to database")
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func autoMigrate(db *gorm.DB) error {
|
||||
models := []interface{}{
|
||||
&model.User{},
|
||||
// Добавьте другие модели здесь
|
||||
}
|
||||
|
||||
for _, m := range models {
|
||||
if err := db.AutoMigrate(m); err != nil {
|
||||
return fmt.Errorf("failed to migrate model: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Database migration completed")
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user