mirror of
https://gitlab.com/foxixus/neomovies-api.git
synced 2025-10-27 17:38:51 +05:00
- Vidsrc: uses IMDb ID for both movies and TV shows
- Vidlink: uses IMDb ID for movies, TMDB ID for TV shows
- Updated routes: /players/vidsrc/{media_type}/{imdb_id}
- Updated routes: /players/vidlink/movie/{imdb_id}
- New route: /players/vidlink/tv/{tmdb_id}
- Added comprehensive OpenAPI documentation for new players
1763 lines
60 KiB
Go
1763 lines
60 KiB
Go
package handlers
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"net/http"
|
||
"strings"
|
||
|
||
"github.com/MarceloPetrucio/go-scalar-api-reference"
|
||
)
|
||
|
||
type DocsHandler struct{}
|
||
|
||
func NewDocsHandler() *DocsHandler {
|
||
return &DocsHandler{}
|
||
}
|
||
|
||
func (h *DocsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||
h.ServeDocs(w, r)
|
||
}
|
||
|
||
func (h *DocsHandler) RedirectToDocs(w http.ResponseWriter, r *http.Request) {
|
||
http.Redirect(w, r, "/docs/", http.StatusMovedPermanently)
|
||
}
|
||
|
||
func (h *DocsHandler) GetOpenAPISpec(w http.ResponseWriter, r *http.Request) {
|
||
_ = determineBaseURL(r)
|
||
|
||
// Use relative server URL to inherit correct scheme/host from the browser/proxy
|
||
spec := getOpenAPISpecWithURL("/")
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Origin, X-Requested-With")
|
||
json.NewEncoder(w).Encode(spec)
|
||
}
|
||
|
||
func (h *DocsHandler) ServeDocs(w http.ResponseWriter, r *http.Request) {
|
||
baseURL := determineBaseURL(r)
|
||
|
||
// Use absolute SpecURL so the library does not try to read a local file path
|
||
htmlContent, err := scalar.ApiReferenceHTML(&scalar.Options{
|
||
SpecURL: fmt.Sprintf("%s/openapi.json", baseURL),
|
||
CustomOptions: scalar.CustomOptions{
|
||
PageTitle: "Neo Movies API Documentation",
|
||
},
|
||
DarkMode: true,
|
||
})
|
||
|
||
if err != nil {
|
||
fmt.Printf("Error generating documentation: %v", err)
|
||
http.Error(w, fmt.Sprintf("Error generating documentation: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||
fmt.Fprintln(w, htmlContent)
|
||
}
|
||
|
||
func determineBaseURL(r *http.Request) string {
|
||
// Prefer proxy headers and request info over environment to avoid wrong scheme on platforms like Vercel
|
||
proto := ""
|
||
host := r.Host
|
||
|
||
if fwd := r.Header.Get("Forwarded"); fwd != "" {
|
||
for _, part := range strings.Split(fwd, ";") {
|
||
kv := strings.SplitN(strings.TrimSpace(part), "=", 2)
|
||
if len(kv) != 2 {
|
||
continue
|
||
}
|
||
key := strings.ToLower(strings.TrimSpace(kv[0]))
|
||
val := strings.Trim(strings.TrimSpace(kv[1]), "\"")
|
||
switch key {
|
||
case "proto":
|
||
if proto == "" {
|
||
proto = strings.ToLower(val)
|
||
}
|
||
case "host":
|
||
if val != "" {
|
||
host = val
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if proto == "" {
|
||
if p := r.Header.Get("X-Forwarded-Proto"); p != "" {
|
||
proto = strings.ToLower(strings.TrimSpace(strings.Split(p, ",")[0]))
|
||
}
|
||
}
|
||
if xfh := r.Header.Get("X-Forwarded-Host"); xfh != "" {
|
||
host = strings.TrimSpace(strings.Split(xfh, ",")[0])
|
||
}
|
||
|
||
if proto == "" {
|
||
if r.TLS != nil {
|
||
proto = "https"
|
||
} else {
|
||
proto = "http"
|
||
}
|
||
}
|
||
|
||
if xfp := r.Header.Get("X-Forwarded-Port"); xfp != "" && !strings.Contains(host, ":") {
|
||
isDefault := (proto == "http" && xfp == "80") || (proto == "https" && xfp == "443")
|
||
if !isDefault {
|
||
host = host + ":" + xfp
|
||
}
|
||
}
|
||
|
||
return fmt.Sprintf("%s://%s", proto, host)
|
||
}
|
||
|
||
type OpenAPISpec struct {
|
||
OpenAPI string `json:"openapi"`
|
||
Info Info `json:"info"`
|
||
Servers []Server `json:"servers"`
|
||
Paths map[string]interface{} `json:"paths"`
|
||
Components Components `json:"components"`
|
||
}
|
||
|
||
type Info struct {
|
||
Title string `json:"title"`
|
||
Description string `json:"description"`
|
||
Version string `json:"version"`
|
||
Contact Contact `json:"contact"`
|
||
}
|
||
|
||
type Contact struct {
|
||
Name string `json:"name"`
|
||
URL string `json:"url"`
|
||
}
|
||
|
||
type Server struct {
|
||
URL string `json:"url"`
|
||
Description string `json:"description"`
|
||
}
|
||
|
||
type Components struct {
|
||
SecuritySchemes map[string]SecurityScheme `json:"securitySchemes"`
|
||
Schemas map[string]interface{} `json:"schemas"`
|
||
}
|
||
|
||
type SecurityScheme struct {
|
||
Type string `json:"type"`
|
||
Scheme string `json:"scheme,omitempty"`
|
||
BearerFormat string `json:"bearerFormat,omitempty"`
|
||
}
|
||
|
||
func getOpenAPISpecWithURL(baseURL string) *OpenAPISpec {
|
||
return &OpenAPISpec{
|
||
OpenAPI: "3.0.0",
|
||
Info: Info{
|
||
Title: "Neo Movies API",
|
||
Description: "Современный API для поиска фильмов и сериалов с интеграцией TMDB и поддержкой авторизации",
|
||
Version: "2.0.0",
|
||
Contact: Contact{
|
||
Name: "API Support",
|
||
URL: "https://github.com/your-username/neomovies-api-go",
|
||
},
|
||
},
|
||
Servers: []Server{
|
||
{
|
||
URL: baseURL,
|
||
Description: "Production server",
|
||
},
|
||
},
|
||
Paths: map[string]interface{}{
|
||
"/api/v1/health": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Health Check",
|
||
"description": "Проверка работоспособности API",
|
||
"tags": []string{"Health"},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "API работает корректно",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/APIResponse",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/search/multi": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Мультипоиск",
|
||
"description": "Поиск фильмов, сериалов и актеров",
|
||
"tags": []string{"Search"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "query",
|
||
"in": "query",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "Поисковый запрос",
|
||
},
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
"description": "Номер страницы",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Результаты поиска",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/categories": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Получить категории",
|
||
"description": "Получение списка категорий фильмов",
|
||
"tags": []string{"Categories"},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список категорий",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"type": "array",
|
||
"items": map[string]interface{}{"$ref": "#/components/schemas/Category"},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/categories/{id}/movies": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Фильмы по категории",
|
||
"description": "Получение фильмов по категории",
|
||
"tags": []string{"Categories"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID категории",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Фильмы категории",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/categories/{id}/media": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Медиа по категории",
|
||
"description": "Получение фильмов или сериалов по категории",
|
||
"tags": []string{"Categories"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID категории",
|
||
},
|
||
{
|
||
"name": "type",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]interface{}{
|
||
"type": "string",
|
||
"enum": []string{"movie", "tv"},
|
||
"default": "movie",
|
||
},
|
||
"description": "Тип медиа: movie или tv",
|
||
},
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]interface{}{
|
||
"type": "integer",
|
||
"default": 1,
|
||
},
|
||
"description": "Номер страницы",
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]interface{}{
|
||
"type": "string",
|
||
"default": "ru-RU",
|
||
},
|
||
"description": "Язык ответа",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Медиа по категории",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/players/alloha/{imdb_id}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Плеер Alloha",
|
||
"description": "Получение плеера Alloha по IMDb ID",
|
||
"tags": []string{"Players"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "imdb_id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "IMDb ID фильма",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Данные плеера",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/players/lumex/{imdb_id}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Плеер Lumex",
|
||
"description": "Получение плеера Lumex по IMDb ID",
|
||
"tags": []string{"Players"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "imdb_id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "IMDb ID фильма",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Данные плеера",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/players/vibix/{imdb_id}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Vibix плеер по IMDb ID",
|
||
"description": "Возвращает HTML-страницу с iframe Vibix для указанного IMDb ID",
|
||
"tags": []string{"Players"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "imdb_id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "IMDb ID, например tt0133093",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "HTML со встроенным Vibix плеером",
|
||
"content": map[string]interface{}{
|
||
"text/html": map[string]interface{}{},
|
||
},
|
||
},
|
||
"404": map[string]interface{}{"description": "Фильм не найден"},
|
||
"503": map[string]interface{}{"description": "VIBIX_TOKEN не настроен"},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/players/vidsrc/{media_type}/{imdb_id}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Vidsrc плеер (английский)",
|
||
"description": "Возвращает HTML-страницу с iframe Vidsrc.to. Использует IMDb ID для фильмов и сериалов. Пример URL для фильма: https://vidsrc.to/embed/movie/tt1234567, для сериала: https://vidsrc.to/embed/tv/tt6385540/1/1",
|
||
"tags": []string{"Players"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "media_type",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]interface{}{"type": "string", "enum": []string{"movie", "tv"}},
|
||
"description": "Тип контента: movie (фильм) или tv (сериал)",
|
||
},
|
||
{
|
||
"name": "imdb_id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "IMDb ID, например tt6385540 (с префиксом tt)",
|
||
},
|
||
{
|
||
"name": "season",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "Номер сезона (обязательно для TV)",
|
||
},
|
||
{
|
||
"name": "episode",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "Номер серии (обязательно для TV)",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "HTML со встроенным Vidsrc плеером",
|
||
"content": map[string]interface{}{
|
||
"text/html": map[string]interface{}{},
|
||
},
|
||
},
|
||
"400": map[string]interface{}{"description": "Отсутствуют обязательные параметры"},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/players/vidlink/movie/{imdb_id}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Vidlink плеер для фильмов (английский)",
|
||
"description": "Возвращает HTML-страницу с iframe Vidlink.pro для фильмов. Использует IMDb ID. Пример URL: https://vidlink.pro/movie/tt1234567",
|
||
"tags": []string{"Players"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "imdb_id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "IMDb ID фильма, например tt1234567 (с префиксом tt)",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "HTML со встроенным Vidlink плеером",
|
||
"content": map[string]interface{}{
|
||
"text/html": map[string]interface{}{},
|
||
},
|
||
},
|
||
"400": map[string]interface{}{"description": "IMDb ID не указан"},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/players/vidlink/tv/{tmdb_id}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Vidlink плеер для сериалов (английский)",
|
||
"description": "Возвращает HTML-страницу с iframe Vidlink.pro для сериалов. Использует TMDB ID (без префикса tt). Пример URL: https://vidlink.pro/tv/94997/1/1",
|
||
"tags": []string{"Players"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "tmdb_id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "TMDB ID сериала, например 94997 (числовой идентификатор без префикса)",
|
||
},
|
||
{
|
||
"name": "season",
|
||
"in": "query",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "Номер сезона (обязательно)",
|
||
},
|
||
{
|
||
"name": "episode",
|
||
"in": "query",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "Номер серии (обязательно)",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "HTML со встроенным Vidlink плеером",
|
||
"content": map[string]interface{}{
|
||
"text/html": map[string]interface{}{},
|
||
},
|
||
},
|
||
"400": map[string]interface{}{"description": "Отсутствуют обязательные параметры (tmdb_id, season, episode)"},
|
||
},
|
||
},
|
||
},
|
||
|
||
|
||
"/api/v1/torrents/search/{imdbId}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Поиск торрентов",
|
||
"description": "Поиск торрентов по IMDB ID",
|
||
"tags": []string{"Torrents"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "imdbId",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "IMDB ID фильма или сериала",
|
||
},
|
||
{
|
||
"name": "type",
|
||
"in": "query",
|
||
"required": true,
|
||
"schema": map[string]interface{}{"type": "string", "enum": []string{"movie", "tv", "serial"}},
|
||
"description": "Тип контента: movie (фильм) или tv/serial (сериал)",
|
||
},
|
||
{
|
||
"name": "season",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]interface{}{"type": "integer"},
|
||
"description": "Номер сезона (для сериалов)",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Результаты поиска торрентов",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/reactions/{mediaType}/{mediaId}/counts": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Количество реакций",
|
||
"description": "Получение количества реакций для медиа",
|
||
"tags": []string{"Reactions"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "mediaType",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "Тип медиа (movie/tv)",
|
||
},
|
||
{
|
||
"name": "mediaId",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "ID медиа",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Количество реакций",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/ReactionCounts",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/images/{size}/{path}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Изображения",
|
||
"description": "Прокси для изображений TMDB",
|
||
"tags": []string{"Images"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "size",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "Размер изображения",
|
||
},
|
||
{
|
||
"name": "path",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "Путь к изображению",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Изображение",
|
||
"content": map[string]interface{}{
|
||
"image/*": map[string]interface{}{},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/auth/register": map[string]interface{}{
|
||
"post": map[string]interface{}{
|
||
"summary": "Регистрация пользователя",
|
||
"description": "Создание нового аккаунта пользователя",
|
||
"tags": []string{"Authentication"},
|
||
"requestBody": map[string]interface{}{
|
||
"required": true,
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/RegisterRequest",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"201": map[string]interface{}{
|
||
"description": "Пользователь успешно зарегистрирован",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/AuthResponse",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"409": map[string]interface{}{
|
||
"description": "Пользователь с таким email уже существует",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/auth/verify": map[string]interface{}{
|
||
"post": map[string]interface{}{
|
||
"tags": []string{"Authentication"},
|
||
"summary": "Подтверждение email",
|
||
"description": "Подтверждение email пользователя с помощью кода",
|
||
"requestBody": map[string]interface{}{
|
||
"required": true,
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"type": "object",
|
||
"required": []string{"email", "code"},
|
||
"properties": map[string]interface{}{
|
||
"email": map[string]interface{}{
|
||
"type": "string",
|
||
"format": "email",
|
||
"description": "Email пользователя",
|
||
"example": "user@example.com",
|
||
},
|
||
"code": map[string]interface{}{
|
||
"type": "string",
|
||
"description": "6-значный код верификации",
|
||
"example": "123456",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Email успешно подтвержден",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"success": map[string]interface{}{
|
||
"type": "boolean",
|
||
},
|
||
"message": map[string]interface{}{
|
||
"type": "string",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"400": map[string]interface{}{
|
||
"description": "Неверный или истекший код",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/auth/resend-code": map[string]interface{}{
|
||
"post": map[string]interface{}{
|
||
"tags": []string{"Authentication"},
|
||
"summary": "Повторная отправка кода",
|
||
"description": "Повторная отправка кода верификации на email",
|
||
"requestBody": map[string]interface{}{
|
||
"required": true,
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"type": "object",
|
||
"required": []string{"email"},
|
||
"properties": map[string]interface{}{
|
||
"email": map[string]interface{}{
|
||
"type": "string",
|
||
"format": "email",
|
||
"description": "Email пользователя",
|
||
"example": "user@example.com",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Код отправлен на email",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"success": map[string]interface{}{
|
||
"type": "boolean",
|
||
},
|
||
"message": map[string]interface{}{
|
||
"type": "string",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"400": map[string]interface{}{
|
||
"description": "Email уже подтвержден или пользователь не найден",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/auth/login": map[string]interface{}{
|
||
"post": map[string]interface{}{
|
||
"summary": "Авторизация пользователя",
|
||
"description": "Получение JWT токена для доступа к приватным эндпоинтам",
|
||
"tags": []string{"Authentication"},
|
||
"requestBody": map[string]interface{}{
|
||
"required": true,
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/LoginRequest",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Успешная авторизация",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/AuthResponse",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"401": map[string]interface{}{
|
||
"description": "Неверный email или пароль",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/auth/profile": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Получить профиль пользователя",
|
||
"description": "Получение информации о текущем пользователе",
|
||
"tags": []string{"Authentication"},
|
||
"security": []map[string][]string{
|
||
{"bearerAuth": []string{}},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Информация о пользователе",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/User",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"put": map[string]interface{}{
|
||
"summary": "Обновить профиль пользователя",
|
||
"description": "Обновление информации о пользователе",
|
||
"tags": []string{"Authentication"},
|
||
"security": []map[string][]string{
|
||
{"bearerAuth": []string{}},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Профиль успешно обновлен",
|
||
},
|
||
},
|
||
},
|
||
"delete": map[string]interface{}{
|
||
"summary": "Удалить аккаунт пользователя",
|
||
"description": "Полное и безвозвратное удаление аккаунта пользователя и всех связанных с ним данных (избранное, реакции)",
|
||
"tags": []string{"Authentication"},
|
||
"security": []map[string][]string{
|
||
{"bearerAuth": []string{}},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Аккаунт успешно удален",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"success": map[string]interface{}{"type": "boolean"},
|
||
"message": map[string]interface{}{"type": "string"},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"401": map[string]interface{}{
|
||
"description": "Неавторизованный запрос",
|
||
},
|
||
"500": map[string]interface{}{
|
||
"description": "Внутренняя ошибка сервера",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/search": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Поиск фильмов",
|
||
"description": "Поиск фильмов по названию с поддержкой фильтров",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "query",
|
||
"in": "query",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "Поисковый запрос",
|
||
},
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
"description": "Номер страницы",
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
"description": "Язык ответа",
|
||
},
|
||
{
|
||
"name": "year",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "Год выпуска",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Результаты поиска фильмов",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/MovieSearchResponse",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/popular": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Популярные фильмы",
|
||
"description": "Получение списка популярных фильмов",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список популярных фильмов",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/{id}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Получить фильм по ID",
|
||
"description": "Подробная информация о фильме",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID фильма в TMDB",
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Информация о фильме",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/Movie",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/favorites": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Получить избранное",
|
||
"description": "Список избранных фильмов и сериалов пользователя",
|
||
"tags": []string{"Favorites"},
|
||
"security": []map[string][]string{
|
||
{"bearerAuth": []string{}},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список избранного",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/favorites/{id}": map[string]interface{}{
|
||
"post": map[string]interface{}{
|
||
"summary": "Добавить в избранное",
|
||
"description": "Добавление фильма или сериала в избранное",
|
||
"tags": []string{"Favorites"},
|
||
"security": []map[string][]string{
|
||
{"bearerAuth": []string{}},
|
||
},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "ID медиа",
|
||
},
|
||
{
|
||
"name": "type",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]interface{}{
|
||
"type": "string",
|
||
"enum": []string{"movie", "tv"},
|
||
"default": "movie",
|
||
},
|
||
"description": "Тип медиа: movie или tv",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Добавлено в избранное",
|
||
},
|
||
},
|
||
},
|
||
"delete": map[string]interface{}{
|
||
"summary": "Удалить из избранного",
|
||
"description": "Удаление фильма или сериала из избранного",
|
||
"tags": []string{"Favorites"},
|
||
"security": []map[string][]string{
|
||
{"bearerAuth": []string{}},
|
||
},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "ID медиа",
|
||
},
|
||
{
|
||
"name": "type",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]interface{}{
|
||
"type": "string",
|
||
"enum": []string{"movie", "tv"},
|
||
"default": "movie",
|
||
},
|
||
"description": "Тип медиа: movie или tv",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Удалено из избранного",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/favorites/{id}/check": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Проверить избранное",
|
||
"description": "Проверка, находится ли медиа в избранном",
|
||
"tags": []string{"Favorites"},
|
||
"security": []map[string][]string{
|
||
{"bearerAuth": []string{}},
|
||
},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "ID медиа",
|
||
},
|
||
{
|
||
"name": "type",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": map[string]interface{}{
|
||
"type": "string",
|
||
"enum": []string{"movie", "tv"},
|
||
"default": "movie",
|
||
},
|
||
"description": "Тип медиа: movie или tv",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Статус избранного",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/top-rated": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Топ рейтинг фильмов",
|
||
"description": "Получение списка фильмов с высоким рейтингом",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список фильмов с высоким рейтингом",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/upcoming": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Скоро в прокате",
|
||
"description": "Получение списка фильмов, которые скоро выйдут в прокат",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список фильмов, которые скоро выйдут",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/now-playing": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Сейчас в прокате",
|
||
"description": "Получение списка фильмов, которые сейчас в прокате",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список фильмов в прокате",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/{id}/recommendations": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Рекомендации фильмов",
|
||
"description": "Получение рекомендаций фильмов на основе выбранного",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID фильма в TMDB",
|
||
},
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Рекомендуемые фильмы",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/{id}/similar": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Похожие фильмы",
|
||
"description": "Получение похожих фильмов",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID фильма в TMDB",
|
||
},
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Похожие фильмы",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/search": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Поиск сериалов",
|
||
"description": "Поиск сериалов по названию",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "query",
|
||
"in": "query",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "string"},
|
||
"description": "Поисковый запрос",
|
||
},
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
"description": "Номер страницы",
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
"description": "Язык ответа",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Результаты поиска сериалов",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/TVSearchResponse",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/popular": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Популярные сериалы",
|
||
"description": "Получение списка популярных сериалов",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список популярных сериалов",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/top-rated": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Топ рейтинг сериалов",
|
||
"description": "Получение списка сериалов с высоким рейтингом",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список сериалов с высоким рейтингом",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/on-the-air": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "В эфире",
|
||
"description": "Получение списка сериалов, которые сейчас в эфире",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список сериалов в эфире",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/airing-today": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Сегодня в эфире",
|
||
"description": "Получение списка сериалов, которые выходят сегодня",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Список сериалов, выходящих сегодня",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/{id}": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Получить сериал по ID",
|
||
"description": "Подробная информация о сериале",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID сериала в TMDB",
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Информация о сериале",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/TVSeries",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/{id}/recommendations": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Рекомендации сериалов",
|
||
"description": "Получение рекомендаций сериалов на основе выбранного",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID сериала в TMDB",
|
||
},
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Рекомендуемые сериалы",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/{id}/similar": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Похожие сериалы",
|
||
"description": "Получение похожих сериалов",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID сериала в TMDB",
|
||
},
|
||
{
|
||
"name": "page",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "integer", "default": "1"},
|
||
},
|
||
{
|
||
"name": "language",
|
||
"in": "query",
|
||
"schema": map[string]string{"type": "string", "default": "ru-RU"},
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Похожие сериалы",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/movies/{id}/external-ids": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Внешние идентификаторы фильма",
|
||
"description": "Получить внешние ID (IMDb, TVDB, Facebook и др.) для фильма по TMDB ID",
|
||
"tags": []string{"Movies"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID фильма в TMDB",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Внешние идентификаторы фильма",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/ExternalIDs",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/tv/{id}/external-ids": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Внешние идентификаторы сериала",
|
||
"description": "Получить внешние ID (IMDb, TVDB, Facebook и др.) для сериала по TMDB ID",
|
||
"tags": []string{"TV Series"},
|
||
"parameters": []map[string]interface{}{
|
||
{
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": map[string]string{"type": "integer"},
|
||
"description": "ID сериала в TMDB",
|
||
},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Внешние идентификаторы сериала",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{
|
||
"$ref": "#/components/schemas/ExternalIDs",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/auth/google/login": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Google OAuth: начало",
|
||
"description": "Редирект на страницу авторизации Google",
|
||
"tags": []string{"Authentication"},
|
||
"responses": map[string]interface{}{
|
||
"302": map[string]interface{}{"description": "Redirect to Google"},
|
||
"400": map[string]interface{}{"description": "OAuth не сконфигурирован"},
|
||
},
|
||
},
|
||
},
|
||
"/api/v1/auth/google/callback": map[string]interface{}{
|
||
"get": map[string]interface{}{
|
||
"summary": "Google OAuth: коллбек",
|
||
"description": "Обработка кода авторизации и выдача JWT",
|
||
"tags": []string{"Authentication"},
|
||
"parameters": []map[string]interface{}{
|
||
{"name": "state", "in": "query", "required": true, "schema": map[string]string{"type": "string"}},
|
||
{"name": "code", "in": "query", "required": true, "schema": map[string]string{"type": "string"}},
|
||
},
|
||
"responses": map[string]interface{}{
|
||
"200": map[string]interface{}{
|
||
"description": "Успешная авторизация через Google",
|
||
"content": map[string]interface{}{
|
||
"application/json": map[string]interface{}{
|
||
"schema": map[string]interface{}{"$ref": "#/components/schemas/AuthResponse"},
|
||
},
|
||
},
|
||
},
|
||
"400": map[string]interface{}{"description": "Неверный state или ошибка обмена кода"},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
Components: Components{
|
||
SecuritySchemes: map[string]SecurityScheme{
|
||
"bearerAuth": {
|
||
Type: "http",
|
||
Scheme: "bearer",
|
||
BearerFormat: "JWT",
|
||
},
|
||
},
|
||
Schemas: map[string]interface{}{
|
||
"APIResponse": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"success": map[string]string{"type": "boolean"},
|
||
"data": map[string]string{"type": "object"},
|
||
"message": map[string]string{"type": "string"},
|
||
"error": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
"RegisterRequest": map[string]interface{}{
|
||
"type": "object",
|
||
"required": []string{"email", "password", "name"},
|
||
"properties": map[string]interface{}{
|
||
"email": map[string]interface{}{
|
||
"type": "string",
|
||
"format": "email",
|
||
"example": "user@example.com",
|
||
},
|
||
"password": map[string]interface{}{
|
||
"type": "string",
|
||
"minLength": 6,
|
||
"example": "password123",
|
||
},
|
||
"name": map[string]interface{}{
|
||
"type": "string",
|
||
"example": "Иван Иванов",
|
||
},
|
||
},
|
||
},
|
||
"LoginRequest": map[string]interface{}{
|
||
"type": "object",
|
||
"required": []string{"email", "password"},
|
||
"properties": map[string]interface{}{
|
||
"email": map[string]interface{}{
|
||
"type": "string",
|
||
"format": "email",
|
||
"example": "user@example.com",
|
||
},
|
||
"password": map[string]interface{}{
|
||
"type": "string",
|
||
"example": "password123",
|
||
},
|
||
},
|
||
},
|
||
"AuthResponse": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"token": map[string]string{"type": "string"},
|
||
"user": map[string]interface{}{"$ref": "#/components/schemas/User"},
|
||
},
|
||
},
|
||
"User": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"id": map[string]string{"type": "string"},
|
||
"email": map[string]string{"type": "string"},
|
||
"name": map[string]string{"type": "string"},
|
||
"avatar": map[string]string{"type": "string"},
|
||
"favorites": map[string]interface{}{
|
||
"type": "array",
|
||
"items": map[string]string{"type": "string"},
|
||
},
|
||
"created_at": map[string]interface{}{
|
||
"type": "string",
|
||
"format": "date-time",
|
||
},
|
||
"updated_at": map[string]interface{}{
|
||
"type": "string",
|
||
"format": "date-time",
|
||
},
|
||
},
|
||
},
|
||
"Movie": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"id": map[string]string{"type": "integer"},
|
||
"title": map[string]string{"type": "string"},
|
||
"original_title": map[string]string{"type": "string"},
|
||
"overview": map[string]string{"type": "string"},
|
||
"poster_path": map[string]string{"type": "string"},
|
||
"backdrop_path": map[string]string{"type": "string"},
|
||
"release_date": map[string]string{"type": "string"},
|
||
"vote_average": map[string]string{"type": "number"},
|
||
"vote_count": map[string]string{"type": "integer"},
|
||
"popularity": map[string]string{"type": "number"},
|
||
"adult": map[string]string{"type": "boolean"},
|
||
"original_language": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
"MovieSearchResponse": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"page": map[string]string{"type": "integer"},
|
||
"results": map[string]interface{}{
|
||
"type": "array",
|
||
"items": map[string]interface{}{"$ref": "#/components/schemas/Movie"},
|
||
},
|
||
"total_pages": map[string]string{"type": "integer"},
|
||
"total_results": map[string]string{"type": "integer"},
|
||
},
|
||
},
|
||
"TVSeries": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"id": map[string]string{"type": "integer"},
|
||
"name": map[string]string{"type": "string"},
|
||
"original_name": map[string]string{"type": "string"},
|
||
"overview": map[string]string{"type": "string"},
|
||
"poster_path": map[string]string{"type": "string"},
|
||
"backdrop_path": map[string]string{"type": "string"},
|
||
"first_air_date": map[string]string{"type": "string"},
|
||
"vote_average": map[string]string{"type": "number"},
|
||
"vote_count": map[string]string{"type": "integer"},
|
||
"popularity": map[string]string{"type": "number"},
|
||
"original_language": map[string]string{"type": "string"},
|
||
"number_of_seasons": map[string]string{"type": "integer"},
|
||
"number_of_episodes": map[string]string{"type": "integer"},
|
||
"status": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
"TVSearchResponse": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"page": map[string]string{"type": "integer"},
|
||
"results": map[string]interface{}{
|
||
"type": "array",
|
||
"items": map[string]interface{}{"$ref": "#/components/schemas/TVSeries"},
|
||
},
|
||
"total_pages": map[string]string{"type": "integer"},
|
||
"total_results": map[string]string{"type": "integer"},
|
||
},
|
||
},
|
||
"Category": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"id": map[string]string{"type": "integer"},
|
||
"name": map[string]string{"type": "string"},
|
||
"description": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
"Player": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"url": map[string]string{"type": "string"},
|
||
"title": map[string]string{"type": "string"},
|
||
"quality": map[string]string{"type": "string"},
|
||
"type": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
"Torrent": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"title": map[string]string{"type": "string"},
|
||
"size": map[string]string{"type": "string"},
|
||
"seeds": map[string]string{"type": "integer"},
|
||
"peers": map[string]string{"type": "integer"},
|
||
"magnet": map[string]string{"type": "string"},
|
||
"hash": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
"Reaction": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"type": map[string]string{"type": "string"},
|
||
"count": map[string]string{"type": "integer"},
|
||
},
|
||
},
|
||
"ReactionCounts": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"like": map[string]string{"type": "integer"},
|
||
"dislike": map[string]string{"type": "integer"},
|
||
"love": map[string]string{"type": "integer"},
|
||
},
|
||
},
|
||
"ExternalIDs": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"id": map[string]string{"type": "integer"},
|
||
"imdb_id": map[string]string{"type": "string"},
|
||
"tvdb_id": map[string]string{"type": "integer"},
|
||
"wikidata_id": map[string]string{"type": "string"},
|
||
"facebook_id": map[string]string{"type": "string"},
|
||
"instagram_id": map[string]string{"type": "string"},
|
||
"twitter_id": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
"WebTorrentMetadata": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"id": map[string]string{"type": "integer"},
|
||
"title": map[string]string{"type": "string"},
|
||
"type": map[string]interface{}{
|
||
"type": "string",
|
||
"enum": []string{"movie", "tv"},
|
||
},
|
||
"year": map[string]string{"type": "integer"},
|
||
"posterPath": map[string]string{"type": "string"},
|
||
"backdropPath": map[string]string{"type": "string"},
|
||
"overview": map[string]string{"type": "string"},
|
||
"runtime": map[string]string{"type": "integer"},
|
||
"genres": map[string]interface{}{
|
||
"type": "array",
|
||
"items": map[string]interface{}{
|
||
"$ref": "#/components/schemas/Genre",
|
||
},
|
||
},
|
||
"seasons": map[string]interface{}{
|
||
"type": "array",
|
||
"items": map[string]interface{}{
|
||
"$ref": "#/components/schemas/SeasonMetadata",
|
||
},
|
||
},
|
||
"episodes": map[string]interface{}{
|
||
"type": "array",
|
||
"items": map[string]interface{}{
|
||
"$ref": "#/components/schemas/EpisodeMetadata",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"SeasonMetadata": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"seasonNumber": map[string]string{"type": "integer"},
|
||
"name": map[string]string{"type": "string"},
|
||
"episodes": map[string]interface{}{
|
||
"type": "array",
|
||
"items": map[string]interface{}{
|
||
"$ref": "#/components/schemas/EpisodeMetadata",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
"EpisodeMetadata": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"episodeNumber": map[string]string{"type": "integer"},
|
||
"seasonNumber": map[string]string{"type": "integer"},
|
||
"name": map[string]string{"type": "string"},
|
||
"overview": map[string]string{"type": "string"},
|
||
"runtime": map[string]string{"type": "integer"},
|
||
"stillPath": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
"Genre": map[string]interface{}{
|
||
"type": "object",
|
||
"properties": map[string]interface{}{
|
||
"id": map[string]string{"type": "integer"},
|
||
"name": map[string]string{"type": "string"},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
}
|