First swagger version, added auto-migrate on startup

This commit is contained in:
2026-03-15 20:40:41 +05:00
parent fb0269f804
commit b38f9cf8fd
18 changed files with 984 additions and 30 deletions

View File

@@ -1,5 +1,345 @@
openapi: 3.0.3
info:
title: logiflow
description: API для информационной системы для логистической компании
version: 1.0.0
title: Logiflow API
description: API для логистической информационной системы
version: 0.1.0
contact:
name: anxi0uz
servers:
- url: http://localhost:3001
description: Local development
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
ApiResponse:
type: object
properties:
status:
type: integer
nullable: false
data:
type: object
nullable: true
success:
type: boolean
nullable: false
requestID:
type: string
nullable: false
ErrorResponse:
type: object
properties:
status:
type: integer
message:
type: string
requestID:
type: string
User:
type: object
properties:
id:
type: string
format: uuid
email:
type: string
format: email
slug:
type: string
fullName:
type: string
nullable: true
avatarUrl:
type: string
nullable: true
passwordHash:
type: string
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
nullable: true
lastLoginAt:
type: string
format: date-time
nullable: true
UserResponse:
type: object
properties:
id:
type: string
format: uuid
email:
type: string
format: email
slug:
type: string
fullName:
type: string
nullable: true
avatarUrl:
type: string
nullable: true
createdAt:
type: string
format: date-time
RegisterRequest:
type: object
required: [email, password, fullName]
properties:
email:
type: string
format: email
password:
type: string
minLength: 8
fullName:
type: string
LoginRequest:
type: object
required: [email, password]
properties:
email:
type: string
format: email
password:
type: string
TokenRefreshRequest:
type: object
required: [refreshToken]
properties:
refreshToken:
type: string
description: Refresh токен, полученный при логине
TokenResponse:
type: object
required: [accessToken, refreshToken]
properties:
accessToken:
type: string
description: Access токен (JWT)
refreshToken:
type: string
description: Refresh токен (opaque token, rotation)
expiresIn:
type: integer
description: Время жизни access токена в секундах
UserUpdate:
type: object
properties:
fullName:
type: string
minLength: 2
maxLength: 150
nullable: true
avatarUrl:
type: string
format: uri
nullable: true
password:
type: string
minLength: 8
description: Новый пароль (если меняется)
currentPassword:
type: string
minLength: 8
description: Текущий пароль (обязателен при смене пароля)
UserDeleteRequest:
type: object
required: [password]
properties:
password:
type: string
description: Текущий пароль для подтверждения удаления
paths:
/auth/register:
post:
operationId: authRegister
summary: Регистрация нового пользователя
tags: [Auth]
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RegisterRequest"
responses:
"201":
description: Пользователь успешно создан
content:
application/json:
schema:
$ref: "#/components/schemas/ApiResponse"
"400":
description: Некорректные данные
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Email уже занят
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/auth/login:
post:
operationId: authLogin
summary: Авторизация пользователя
tags: [Auth]
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/LoginRequest"
responses:
"200":
description: Успешный вход
content:
application/json:
schema:
$ref: "#/components/schemas/ApiResponse"
"401":
description: Неверный email или пароль
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/auth/refresh:
post:
operationId: authRefresh
summary: Обновление access-токена через refresh-токен
tags: [Auth]
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/TokenRefreshRequest"
responses:
"200":
description: Токены успешно обновлены
content:
application/json:
schema:
$ref: "#/components/schemas/ApiResponse"
"401":
description: Недействительный или истёкший refresh-токен
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"400":
description: Некорректный запрос
/auth/logout:
post:
operationId: authLogout
summary: Выход из системы
tags: [Auth]
security:
- BearerAuth: []
responses:
"204":
description: Успешный выход
"401":
description: Не авторизован
/me:
get:
operationId: getMe
summary: Получить текущего пользователя
tags: [Me]
security:
- BearerAuth: []
responses:
"200":
description: Данные пользователя
content:
application/json:
schema:
$ref: "#/components/schemas/ApiResponse"
"401":
description: Не авторизован
patch:
operationId: updateMe
summary: Обновить данные текущего пользователя
tags: [Me]
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UserUpdate"
responses:
"200":
description: Пользователь обновлён
content:
application/json:
schema:
$ref: "#/components/schemas/ApiResponse"
"400":
description: Некорректные данные
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Не авторизован
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
delete:
operationId: deleteMe
summary: Удалить аккаунт
tags: [Me]
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UserDeleteRequest"
responses:
"204":
description: Аккаунт удалён
"400":
description: Неверный пароль
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Не авторизован
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"

424
internal/api/gen.go Normal file
View File

@@ -0,0 +1,424 @@
// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.6.0 DO NOT EDIT.
package api
import (
"context"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
openapi_types "github.com/oapi-codegen/runtime/types"
)
const (
BearerAuthScopes = "BearerAuth.Scopes"
)
// ApiResponse defines model for ApiResponse.
type ApiResponse struct {
Data *map[string]interface{} `json:"data,omitempty"`
RequestID *string `json:"requestID,omitempty"`
Status *int `json:"status,omitempty"`
Success *bool `json:"success,omitempty"`
}
// ErrorResponse defines model for ErrorResponse.
type ErrorResponse struct {
Message *string `json:"message,omitempty"`
RequestID *string `json:"requestID,omitempty"`
Status *int `json:"status,omitempty"`
}
// LoginRequest defines model for LoginRequest.
type LoginRequest struct {
Email openapi_types.Email `json:"email"`
Password string `json:"password"`
}
// RegisterRequest defines model for RegisterRequest.
type RegisterRequest struct {
Email openapi_types.Email `json:"email"`
FullName string `json:"fullName"`
Password string `json:"password"`
}
// TokenRefreshRequest defines model for TokenRefreshRequest.
type TokenRefreshRequest struct {
// RefreshToken Refresh токен, полученный при логине
RefreshToken string `json:"refreshToken"`
}
// UserDeleteRequest defines model for UserDeleteRequest.
type UserDeleteRequest struct {
// Password Текущий пароль для подтверждения удаления
Password string `json:"password"`
}
// UserUpdate defines model for UserUpdate.
type UserUpdate struct {
AvatarUrl *string `json:"avatarUrl,omitempty"`
// CurrentPassword Текущий пароль (обязателен при смене пароля)
CurrentPassword *string `json:"currentPassword,omitempty"`
FullName *string `json:"fullName,omitempty"`
// Password Новый пароль (если меняется)
Password *string `json:"password,omitempty"`
}
// AuthLoginJSONRequestBody defines body for AuthLogin for application/json ContentType.
type AuthLoginJSONRequestBody = LoginRequest
// AuthRefreshJSONRequestBody defines body for AuthRefresh for application/json ContentType.
type AuthRefreshJSONRequestBody = TokenRefreshRequest
// AuthRegisterJSONRequestBody defines body for AuthRegister for application/json ContentType.
type AuthRegisterJSONRequestBody = RegisterRequest
// DeleteMeJSONRequestBody defines body for DeleteMe for application/json ContentType.
type DeleteMeJSONRequestBody = UserDeleteRequest
// UpdateMeJSONRequestBody defines body for UpdateMe for application/json ContentType.
type UpdateMeJSONRequestBody = UserUpdate
// ServerInterface represents all server handlers.
type ServerInterface interface {
// Авторизация пользователя
// (POST /auth/login)
AuthLogin(w http.ResponseWriter, r *http.Request)
// Выход из системы
// (POST /auth/logout)
AuthLogout(w http.ResponseWriter, r *http.Request)
// Обновление access-токена через refresh-токен
// (POST /auth/refresh)
AuthRefresh(w http.ResponseWriter, r *http.Request)
// Регистрация нового пользователя
// (POST /auth/register)
AuthRegister(w http.ResponseWriter, r *http.Request)
// Удалить аккаунт
// (DELETE /me)
DeleteMe(w http.ResponseWriter, r *http.Request)
// Получить текущего пользователя
// (GET /me)
GetMe(w http.ResponseWriter, r *http.Request)
// Обновить данные текущего пользователя
// (PATCH /me)
UpdateMe(w http.ResponseWriter, r *http.Request)
}
// Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint.
type Unimplemented struct{}
// Авторизация пользователя
// (POST /auth/login)
func (_ Unimplemented) AuthLogin(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
// Выход из системы
// (POST /auth/logout)
func (_ Unimplemented) AuthLogout(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
// Обновление access-токена через refresh-токен
// (POST /auth/refresh)
func (_ Unimplemented) AuthRefresh(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
// Регистрация нового пользователя
// (POST /auth/register)
func (_ Unimplemented) AuthRegister(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
// Удалить аккаунт
// (DELETE /me)
func (_ Unimplemented) DeleteMe(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
// Получить текущего пользователя
// (GET /me)
func (_ Unimplemented) GetMe(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
// Обновить данные текущего пользователя
// (PATCH /me)
func (_ Unimplemented) UpdateMe(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
// ServerInterfaceWrapper converts contexts to parameters.
type ServerInterfaceWrapper struct {
Handler ServerInterface
HandlerMiddlewares []MiddlewareFunc
ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
}
type MiddlewareFunc func(http.Handler) http.Handler
// AuthLogin operation middleware
func (siw *ServerInterfaceWrapper) AuthLogin(w http.ResponseWriter, r *http.Request) {
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.AuthLogin(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// AuthLogout operation middleware
func (siw *ServerInterfaceWrapper) AuthLogout(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
r = r.WithContext(ctx)
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.AuthLogout(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// AuthRefresh operation middleware
func (siw *ServerInterfaceWrapper) AuthRefresh(w http.ResponseWriter, r *http.Request) {
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.AuthRefresh(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// AuthRegister operation middleware
func (siw *ServerInterfaceWrapper) AuthRegister(w http.ResponseWriter, r *http.Request) {
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.AuthRegister(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// DeleteMe operation middleware
func (siw *ServerInterfaceWrapper) DeleteMe(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
r = r.WithContext(ctx)
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.DeleteMe(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// GetMe operation middleware
func (siw *ServerInterfaceWrapper) GetMe(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
r = r.WithContext(ctx)
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.GetMe(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// UpdateMe operation middleware
func (siw *ServerInterfaceWrapper) UpdateMe(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
r = r.WithContext(ctx)
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.UpdateMe(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
type UnescapedCookieParamError struct {
ParamName string
Err error
}
func (e *UnescapedCookieParamError) Error() string {
return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName)
}
func (e *UnescapedCookieParamError) Unwrap() error {
return e.Err
}
type UnmarshalingParamError struct {
ParamName string
Err error
}
func (e *UnmarshalingParamError) Error() string {
return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error())
}
func (e *UnmarshalingParamError) Unwrap() error {
return e.Err
}
type RequiredParamError struct {
ParamName string
}
func (e *RequiredParamError) Error() string {
return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName)
}
type RequiredHeaderError struct {
ParamName string
Err error
}
func (e *RequiredHeaderError) Error() string {
return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName)
}
func (e *RequiredHeaderError) Unwrap() error {
return e.Err
}
type InvalidParamFormatError struct {
ParamName string
Err error
}
func (e *InvalidParamFormatError) Error() string {
return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error())
}
func (e *InvalidParamFormatError) Unwrap() error {
return e.Err
}
type TooManyValuesForParamError struct {
ParamName string
Count int
}
func (e *TooManyValuesForParamError) Error() string {
return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count)
}
// Handler creates http.Handler with routing matching OpenAPI spec.
func Handler(si ServerInterface) http.Handler {
return HandlerWithOptions(si, ChiServerOptions{})
}
type ChiServerOptions struct {
BaseURL string
BaseRouter chi.Router
Middlewares []MiddlewareFunc
ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
}
// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
return HandlerWithOptions(si, ChiServerOptions{
BaseRouter: r,
})
}
func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler {
return HandlerWithOptions(si, ChiServerOptions{
BaseURL: baseURL,
BaseRouter: r,
})
}
// HandlerWithOptions creates http.Handler with additional options
func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler {
r := options.BaseRouter
if r == nil {
r = chi.NewRouter()
}
if options.ErrorHandlerFunc == nil {
options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
http.Error(w, err.Error(), http.StatusBadRequest)
}
}
wrapper := ServerInterfaceWrapper{
Handler: si,
HandlerMiddlewares: options.Middlewares,
ErrorHandlerFunc: options.ErrorHandlerFunc,
}
r.Group(func(r chi.Router) {
r.Post(options.BaseURL+"/auth/login", wrapper.AuthLogin)
})
r.Group(func(r chi.Router) {
r.Post(options.BaseURL+"/auth/logout", wrapper.AuthLogout)
})
r.Group(func(r chi.Router) {
r.Post(options.BaseURL+"/auth/refresh", wrapper.AuthRefresh)
})
r.Group(func(r chi.Router) {
r.Post(options.BaseURL+"/auth/register", wrapper.AuthRegister)
})
r.Group(func(r chi.Router) {
r.Delete(options.BaseURL+"/me", wrapper.DeleteMe)
})
r.Group(func(r chi.Router) {
r.Get(options.BaseURL+"/me", wrapper.GetMe)
})
r.Group(func(r chi.Router) {
r.Patch(options.BaseURL+"/me", wrapper.UpdateMe)
})
return r
}