fix: end of line char

This commit is contained in:
Josepablo Cruz
2026-03-31 21:01:07 -06:00
parent a95c4d023e
commit 0ca5423776
11 changed files with 567 additions and 562 deletions

View File

@@ -1,3 +1,3 @@
PHONY: gen_openapi_models
gen_openapi_models: openapi/cfg.yaml openapi/openapi.yaml
oapi-codegen.exe -config .\openapi\cfg.yaml .\openapi\openapi.yaml
PHONY: gen_openapi_models
gen_openapi_models: openapi/cfg.yaml openapi/openapi.yaml
oapi-codegen.exe -config .\openapi\cfg.yaml .\openapi\openapi.yaml

View File

@@ -1,24 +1,29 @@
# ETA API v2
This is the second generation of the ETA API developed on GoLang using OpenAPI to auto document and generate api endpoints.
# OpenApi Generator
With the following command the generator tool can be installed
```.{sh}
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
```
# Deployment
The details on how to deploy are described here
## DOTENV
```{.sh}
SQL_DSN="{USER}:{PASS}@tcp({HOST}:{PORT})/{DB}?charset=utf8mb4&parseTime=true&loc=Local"
```
# ETA API v2
This is the second generation of the ETA API developed on GoLang using OpenAPI to auto document and generate api endpoints.
# OpenApi Generator
With the following command the generator tool can be installed
```.{sh}
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
```
# Deployment
The details on how to deploy are described here
## DOTENV
```{.sh}
SQL_DSN="{USER}:{PASS}@tcp({HOST}:{PORT})/{DB}?charset=utf8mb4&parseTime=true&loc=Local"
```
# GPU information
https://en.wikipedia.org/wiki/Graphics_processing_unit ->
https://en.wikipedia.org/wiki/Shader -> https://en.wikipedia.org/wiki/RenderMan_Interface_Specification

View File

@@ -1,42 +1,42 @@
package database
import (
"log"
"github.com/joho/godotenv"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var shared_db_connection *gorm.DB
func initDB() *gorm.DB {
// load .env into environment (so viper can read them)
if err := godotenv.Load(); err != nil {
log.Println(".env not found or could not be loaded; proceeding with existing environment variables")
}
viper.AutomaticEnv() // read from environment
sql_dsn := viper.GetString("SQL_DSN")
if sql_dsn == "" {
log.Fatal("SQL_DSN must be set in .env or environment")
}
db, err := gorm.Open(mysql.Open(sql_dsn), &gorm.Config{})
if err != nil {
log.Fatalf("failed to connect to MySQL Server: %v", err)
}
log.Println("connected to MySQL Server")
return db
}
func Init() *gorm.DB {
if shared_db_connection == nil {
shared_db_connection = initDB()
}
return shared_db_connection
}
package database
import (
"log"
"github.com/joho/godotenv"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var shared_db_connection *gorm.DB
func initDB() *gorm.DB {
// load .env into environment (so viper can read them)
if err := godotenv.Load(); err != nil {
log.Println(".env not found or could not be loaded; proceeding with existing environment variables")
}
viper.AutomaticEnv() // read from environment
sql_dsn := viper.GetString("SQL_DSN")
if sql_dsn == "" {
log.Fatal("SQL_DSN must be set in .env or environment")
}
db, err := gorm.Open(mysql.Open(sql_dsn), &gorm.Config{})
if err != nil {
log.Fatalf("failed to connect to MySQL Server: %v", err)
}
log.Println("connected to MySQL Server")
return db
}
func Init() *gorm.DB {
if shared_db_connection == nil {
shared_db_connection = initDB()
}
return shared_db_connection
}

View File

@@ -1,124 +1,124 @@
/**
* @file schema.rbac.go
* @brief RBAC schema models for GORM
*
* This file defines the base database models used by the RBAC
* (Role-Based Access Control) system. Models map to the following
* tables: user_types, users, auth_identities, auth_credentials,
* roles, permissions, role_permissions and user_roles.
*
* The structs include GORM tags for column names and relationships:
* - UserType: types of users.
* - User: main user record; links to UserType, AuthIdentity and UserRole.
* - AuthIdentity: external identity providers; links to AuthCredential.
* - AuthCredential: stored credentials for an identity.
* - Role: role definitions and their permissions and assigned users.
* - Permission: permission definitions.
* - RolePermission: join table between Role and Permission.
* - UserRole: join table between User and Role with optional expiration.
*
* These models are intended for use with GORM to perform ORM operations
* against the RBAC schema.
*/
package rbac
import (
"time"
)
type UserType struct {
ID uint `gorm:"primaryKey;column:id"`
Name string `gorm:"type:text;column:name"`
Description *string `gorm:"type:text;column:description"`
}
func (UserType) TableName() string { return "user_types" }
type User struct {
ID uint `gorm:"primaryKey;column:id"`
UserTypeID uint `gorm:"column:user_type"`
Name string `gorm:"type:text;column:name"`
LastName string `gorm:"type:text;column:last_name"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
UserType UserType `gorm:"foreignKey:UserTypeID;references:ID"`
AuthIdentities []AuthIdentity `gorm:"foreignKey:UserID;references:ID"`
UserRoles []UserRole `gorm:"foreignKey:UserID;references:ID"`
}
func (User) TableName() string { return "users" }
type AuthIdentity struct {
ID uint `gorm:"primaryKey;column:id"`
UserID uint `gorm:"column:user_id"`
Provider string `gorm:"type:text;column:provider"`
Identifier string `gorm:"type:text;column:identifier"`
IsPrimary bool `gorm:"column:is_primary"`
IsVerified bool `gorm:"column:is_verified"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
User User `gorm:"foreignKey:UserID;references:ID"`
Credentials []AuthCredential `gorm:"foreignKey:IdentityID;references:ID"`
}
func (AuthIdentity) TableName() string { return "auth_identities" }
type AuthCredential struct {
ID uint `gorm:"primaryKey;column:id"`
IdentityID uint `gorm:"column:identity_id"`
Password string `gorm:"type:text;column:password"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
Identity AuthIdentity `gorm:"foreignKey:IdentityID;references:ID"`
}
func (AuthCredential) TableName() string { return "auth_credentials" }
type Role struct {
ID uint `gorm:"primaryKey;column:id"`
Name string `gorm:"type:text;column:name"`
Description *string `gorm:"type:text;column:description"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
RolePermissions []RolePermission `gorm:"foreignKey:RoleID;references:ID"`
UserRoles []UserRole `gorm:"foreignKey:RoleID;references:ID"`
}
func (Role) TableName() string { return "roles" }
type Permission struct {
ID uint `gorm:"primaryKey;column:id"`
Name string `gorm:"type:text;column:name"`
Description *string `gorm:"type:text;column:description"`
RolePermissions []RolePermission `gorm:"foreignKey:PermissionID;references:ID"`
}
func (Permission) TableName() string { return "permissions" }
type RolePermission struct {
ID uint `gorm:"primaryKey;column:id"`
RoleID uint `gorm:"column:role_id"`
PermissionID uint `gorm:"column:permission_id"`
Role Role `gorm:"foreignKey:RoleID;references:ID"`
Permission Permission `gorm:"foreignKey:PermissionID;references:ID"`
}
func (RolePermission) TableName() string { return "role_permissions" }
type UserRole struct {
ID uint `gorm:"primaryKey;column:id"`
UserID uint `gorm:"column:user_id"`
RoleID uint `gorm:"column:role_id"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
ExpiresAt *time.Time `gorm:"column:expires_at"`
User User `gorm:"foreignKey:UserID;references:ID"`
Role Role `gorm:"foreignKey:RoleID;references:ID"`
}
func (UserRole) TableName() string { return "user_roles" }
/**
* @file schema.rbac.go
* @brief RBAC schema models for GORM
*
* This file defines the base database models used by the RBAC
* (Role-Based Access Control) system. Models map to the following
* tables: user_types, users, auth_identities, auth_credentials,
* roles, permissions, role_permissions and user_roles.
*
* The structs include GORM tags for column names and relationships:
* - UserType: types of users.
* - User: main user record; links to UserType, AuthIdentity and UserRole.
* - AuthIdentity: external identity providers; links to AuthCredential.
* - AuthCredential: stored credentials for an identity.
* - Role: role definitions and their permissions and assigned users.
* - Permission: permission definitions.
* - RolePermission: join table between Role and Permission.
* - UserRole: join table between User and Role with optional expiration.
*
* These models are intended for use with GORM to perform ORM operations
* against the RBAC schema.
*/
package rbac
import (
"time"
)
type UserType struct {
ID uint `gorm:"primaryKey;column:id"`
Name string `gorm:"type:text;column:name"`
Description *string `gorm:"type:text;column:description"`
}
func (UserType) TableName() string { return "user_types" }
type User struct {
ID uint `gorm:"primaryKey;column:id"`
UserTypeID uint `gorm:"column:user_type"`
Name string `gorm:"type:text;column:name"`
LastName string `gorm:"type:text;column:last_name"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
UserType UserType `gorm:"foreignKey:UserTypeID;references:ID"`
AuthIdentities []AuthIdentity `gorm:"foreignKey:UserID;references:ID"`
UserRoles []UserRole `gorm:"foreignKey:UserID;references:ID"`
}
func (User) TableName() string { return "users" }
type AuthIdentity struct {
ID uint `gorm:"primaryKey;column:id"`
UserID uint `gorm:"column:user_id"`
Provider string `gorm:"type:text;column:provider"`
Identifier string `gorm:"type:text;column:identifier"`
IsPrimary bool `gorm:"column:is_primary"`
IsVerified bool `gorm:"column:is_verified"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
User User `gorm:"foreignKey:UserID;references:ID"`
Credentials []AuthCredential `gorm:"foreignKey:IdentityID;references:ID"`
}
func (AuthIdentity) TableName() string { return "auth_identities" }
type AuthCredential struct {
ID uint `gorm:"primaryKey;column:id"`
IdentityID uint `gorm:"column:identity_id"`
Password string `gorm:"type:text;column:password"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
Identity AuthIdentity `gorm:"foreignKey:IdentityID;references:ID"`
}
func (AuthCredential) TableName() string { return "auth_credentials" }
type Role struct {
ID uint `gorm:"primaryKey;column:id"`
Name string `gorm:"type:text;column:name"`
Description *string `gorm:"type:text;column:description"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
RolePermissions []RolePermission `gorm:"foreignKey:RoleID;references:ID"`
UserRoles []UserRole `gorm:"foreignKey:RoleID;references:ID"`
}
func (Role) TableName() string { return "roles" }
type Permission struct {
ID uint `gorm:"primaryKey;column:id"`
Name string `gorm:"type:text;column:name"`
Description *string `gorm:"type:text;column:description"`
RolePermissions []RolePermission `gorm:"foreignKey:PermissionID;references:ID"`
}
func (Permission) TableName() string { return "permissions" }
type RolePermission struct {
ID uint `gorm:"primaryKey;column:id"`
RoleID uint `gorm:"column:role_id"`
PermissionID uint `gorm:"column:permission_id"`
Role Role `gorm:"foreignKey:RoleID;references:ID"`
Permission Permission `gorm:"foreignKey:PermissionID;references:ID"`
}
func (RolePermission) TableName() string { return "role_permissions" }
type UserRole struct {
ID uint `gorm:"primaryKey;column:id"`
UserID uint `gorm:"column:user_id"`
RoleID uint `gorm:"column:role_id"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
ExpiresAt *time.Time `gorm:"column:expires_at"`
User User `gorm:"foreignKey:UserID;references:ID"`
Role Role `gorm:"foreignKey:RoleID;references:ID"`
}
func (UserRole) TableName() string { return "user_roles" }

View File

@@ -1,52 +1,52 @@
package auth
import (
"cloud.etaviaporte.com/api/libs/openapi"
"github.com/gofiber/fiber/v2"
)
type Handler struct{}
// GetAuthMe implements openapi.ServerInterface.
func (handler Handler) GetAuthMe(c *fiber.Ctx) error {
err_code := fiber.StatusInternalServerError
code := "Something went wrong!"
message := "This endpoint is not yet implemented"
return c.Status(err_code).JSON(openapi.Error{
Code: &code,
Message: &message,
})
}
// PostAuthLogin implements openapi.ServerInterface.
func (handler Handler) PostAuthLogin(c *fiber.Ctx) error {
err_code := fiber.StatusInternalServerError
code := "Something went wrong!"
message := "This endpoint is not yet implemented"
return c.Status(err_code).JSON(openapi.Error{
Code: &code,
Message: &message,
})
}
// PostAuthLogout implements openapi.ServerInterface.
func (handler Handler) PostAuthLogout(c *fiber.Ctx) error {
err_code := fiber.StatusInternalServerError
code := "Something went wrong!"
message := "This endpoint is not yet implemented"
return c.Status(err_code).JSON(openapi.Error{
Code: &code,
Message: &message,
})
}
// PostAuthRefresh implements openapi.ServerInterface.
func (handler Handler) PostAuthRefresh(c *fiber.Ctx) error {
err_code := fiber.StatusInternalServerError
code := "Something went wrong!"
message := "This endpoint is not yet implemented"
return c.Status(err_code).JSON(openapi.Error{
Code: &code,
Message: &message,
})
}
package auth
import (
"cloud.etaviaporte.com/api/libs/openapi"
"github.com/gofiber/fiber/v2"
)
type Handler struct{}
// GetAuthMe implements openapi.ServerInterface.
func (handler Handler) GetAuthMe(c *fiber.Ctx) error {
err_code := fiber.StatusInternalServerError
code := "Something went wrong!"
message := "This endpoint is not yet implemented"
return c.Status(err_code).JSON(openapi.Error{
Code: &code,
Message: &message,
})
}
// PostAuthLogin implements openapi.ServerInterface.
func (handler Handler) PostAuthLogin(c *fiber.Ctx) error {
err_code := fiber.StatusInternalServerError
code := "Something went wrong!"
message := "This endpoint is not yet implemented"
return c.Status(err_code).JSON(openapi.Error{
Code: &code,
Message: &message,
})
}
// PostAuthLogout implements openapi.ServerInterface.
func (handler Handler) PostAuthLogout(c *fiber.Ctx) error {
err_code := fiber.StatusInternalServerError
code := "Something went wrong!"
message := "This endpoint is not yet implemented"
return c.Status(err_code).JSON(openapi.Error{
Code: &code,
Message: &message,
})
}
// PostAuthRefresh implements openapi.ServerInterface.
func (handler Handler) PostAuthRefresh(c *fiber.Ctx) error {
err_code := fiber.StatusInternalServerError
code := "Something went wrong!"
message := "This endpoint is not yet implemented"
return c.Status(err_code).JSON(openapi.Error{
Code: &code,
Message: &message,
})
}

View File

@@ -1,41 +1,41 @@
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/helmet"
"github.com/gofiber/fiber/v2/middleware/logger"
"cloud.etaviaporte.com/api/libs/services/auth"
)
func main() {
app := fiber.New()
app.Use(cors.New(cors.Config{
AllowOrigins: "https://api.etaviaporte.com, http://127.0.0.1, http://localhost",
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
AllowMethods: "GET, POST, HEAD, PUT, DELETE, PATCH",
AllowCredentials: true,
}))
app.Use(logger.New(logger.Config{
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
app.Use(helmet.New())
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
handler := auth.Handler{}
// openapi.RegisterHandlers(app, auth.Handler{})
app.Post("/auth/login", handler.PostAuthLogin)
app.Post("/auth/logout", handler.PostAuthLogout)
app.Get("/auth/me", handler.GetAuthMe)
app.Post("/auth/refresh", handler.PostAuthRefresh)
app.Listen(":3000")
}
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/helmet"
"github.com/gofiber/fiber/v2/middleware/logger"
"cloud.etaviaporte.com/api/libs/services/auth"
)
func main() {
app := fiber.New()
app.Use(cors.New(cors.Config{
AllowOrigins: "https://api.etaviaporte.com, http://127.0.0.1, http://localhost",
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
AllowMethods: "GET, POST, HEAD, PUT, DELETE, PATCH",
AllowCredentials: true,
}))
app.Use(logger.New(logger.Config{
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
app.Use(helmet.New())
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
handler := auth.Handler{}
// openapi.RegisterHandlers(app, auth.Handler{})
app.Post("/auth/login", handler.PostAuthLogin)
app.Post("/auth/logout", handler.PostAuthLogout)
app.Get("/auth/me", handler.GetAuthMe)
app.Post("/auth/refresh", handler.PostAuthRefresh)
app.Listen(":3000")
}

View File

@@ -1,6 +1,6 @@
package: openapi
output: libs/openapi/generated.go
generate:
models: true
output-options:
skip-prune: true
package: openapi
output: libs/openapi/generated.go
generate:
models: true
output-options:
skip-prune: true

View File

@@ -1,182 +1,182 @@
openapi: 3.0.3
info:
title: Authorization API
version: "1.0.0"
description: Simple authorization endpoints for login, refresh, logout and getting current user info.
servers:
- url: http://localhost:8080
description: Local development server
paths:
/auth/login:
post:
summary: Obtain access and refresh tokens
tags:
- Auth
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: Tokens issued
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/auth/refresh:
post:
summary: Refresh access token using a refresh token
tags:
- Auth
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RefreshRequest'
responses:
'200':
description: New tokens
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/auth/logout:
post:
summary: Revoke refresh token / logout
tags:
- Auth
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RevokeRequest'
responses:
'204':
description: Successfully logged out (no content)
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/auth/me:
get:
summary: Get current authenticated user
tags:
- Auth
security:
- bearerAuth: []
responses:
'200':
description: Current user profile
content:
application/json:
schema:
$ref: '#/components/schemas/UserProfile'
'401':
$ref: '#/components/responses/Unauthorized'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
LoginRequest:
type: object
required:
- username
- password
properties:
username:
type: string
example: user@example.com
password:
type: string
format: password
example: secret123
TokenResponse:
type: object
properties:
accessToken:
type: string
example: eyJhbGciOi...
refreshToken:
type: string
example: dummyr3fr3sht0k3n
expiresIn:
type: integer
description: Seconds until access token expiration
example: 3600
RefreshRequest:
type: object
required:
- refreshToken
properties:
refreshToken:
type: string
RevokeRequest:
type: object
required:
- refreshToken
properties:
refreshToken:
type: string
UserProfile:
type: object
properties:
id:
type: string
example: "123e4567-e89b-12d3-a456-426614174000"
username:
type: string
example: user@example.com
email:
type: string
example: user@example.com
Error:
type: object
properties:
code:
type: string
example: invalid_request
message:
type: string
example: "Detailed error message"
responses:
BadRequest:
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Authentication failed or missing credentials
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
openapi: 3.0.3
info:
title: Authorization API
version: "1.0.0"
description: Simple authorization endpoints for login, refresh, logout and getting current user info.
servers:
- url: http://localhost:8080
description: Local development server
paths:
/auth/login:
post:
summary: Obtain access and refresh tokens
tags:
- Auth
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: Tokens issued
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/auth/refresh:
post:
summary: Refresh access token using a refresh token
tags:
- Auth
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RefreshRequest'
responses:
'200':
description: New tokens
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/auth/logout:
post:
summary: Revoke refresh token / logout
tags:
- Auth
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RevokeRequest'
responses:
'204':
description: Successfully logged out (no content)
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/auth/me:
get:
summary: Get current authenticated user
tags:
- Auth
security:
- bearerAuth: []
responses:
'200':
description: Current user profile
content:
application/json:
schema:
$ref: '#/components/schemas/UserProfile'
'401':
$ref: '#/components/responses/Unauthorized'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
LoginRequest:
type: object
required:
- username
- password
properties:
username:
type: string
example: user@example.com
password:
type: string
format: password
example: secret123
TokenResponse:
type: object
properties:
accessToken:
type: string
example: eyJhbGciOi...
refreshToken:
type: string
example: dummyr3fr3sht0k3n
expiresIn:
type: integer
description: Seconds until access token expiration
example: 3600
RefreshRequest:
type: object
required:
- refreshToken
properties:
refreshToken:
type: string
RevokeRequest:
type: object
required:
- refreshToken
properties:
refreshToken:
type: string
UserProfile:
type: object
properties:
id:
type: string
example: "123e4567-e89b-12d3-a456-426614174000"
username:
type: string
example: user@example.com
email:
type: string
example: user@example.com
Error:
type: object
properties:
code:
type: string
example: invalid_request
message:
type: string
example: "Detailed error message"
responses:
BadRequest:
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Authentication failed or missing credentials
content:
application/json:
schema:
$ref: '#/components/schemas/Error'