mirror of
https://gitlab.com/foxixus/neomovies-api.git
synced 2025-10-28 01:48:51 +05:00
Update 4 files
- /src/index.js - /src/routes/movies.js - /src/config/tmdb.js - /vercel.json
This commit is contained in:
@@ -7,13 +7,27 @@ class TMDBClient {
|
|||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
}
|
},
|
||||||
|
timeout: 10000 // 10 секунд таймаут
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Добавляем интерцептор для обработки ошибок
|
||||||
|
this.client.interceptors.response.use(
|
||||||
|
response => response,
|
||||||
|
error => {
|
||||||
|
console.error('TMDB API Error:', {
|
||||||
|
status: error.response?.status,
|
||||||
|
data: error.response?.data,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async makeRequest(method, endpoint, params = {}) {
|
async makeRequest(method, endpoint, params = {}) {
|
||||||
try {
|
try {
|
||||||
const response = await this.client.request({
|
const response = await this.client({
|
||||||
method,
|
method,
|
||||||
url: endpoint,
|
url: endpoint,
|
||||||
params: {
|
params: {
|
||||||
@@ -22,10 +36,12 @@ class TMDBClient {
|
|||||||
region: 'RU'
|
region: 'RU'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return response.data;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`TMDB API Error: ${error.message}`);
|
if (error.response) {
|
||||||
throw error;
|
throw new Error(`TMDB API Error: ${error.response.data.status_message || error.message}`);
|
||||||
|
}
|
||||||
|
throw new Error(`Network Error: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,71 +51,55 @@ class TMDBClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchMovies(query, page = 1) {
|
async searchMovies(query, page = 1) {
|
||||||
const data = await this.makeRequest('GET', '/search/movie', {
|
const response = await this.makeRequest('GET', '/search/movie', {
|
||||||
query,
|
query,
|
||||||
page,
|
page,
|
||||||
include_adult: false
|
include_adult: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
// Фильтруем результаты
|
// Фильтруем результаты
|
||||||
data.results = data.results.filter(movie =>
|
data.results = data.results.filter(movie =>
|
||||||
movie.poster_path &&
|
movie.poster_path &&
|
||||||
movie.overview &&
|
movie.overview &&
|
||||||
movie.vote_average > 0
|
movie.vote_average > 0
|
||||||
);
|
).map(movie => ({
|
||||||
|
|
||||||
// Добавляем полные URL для изображений
|
|
||||||
data.results = data.results.map(movie => ({
|
|
||||||
...movie,
|
...movie,
|
||||||
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
||||||
backdrop_path: this.getImageURL(movie.backdrop_path, 'w1280')
|
backdrop_path: this.getImageURL(movie.backdrop_path, 'original')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return data;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMovie(id) {
|
async getMovie(id) {
|
||||||
const movie = await this.makeRequest('GET', `/movie/${id}`);
|
const response = await this.makeRequest('GET', `/movie/${id}`);
|
||||||
|
const movie = response.data;
|
||||||
return {
|
return {
|
||||||
...movie,
|
...movie,
|
||||||
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
||||||
backdrop_path: this.getImageURL(movie.backdrop_path, 'w1280')
|
backdrop_path: this.getImageURL(movie.backdrop_path, 'original')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPopularMovies(page = 1) {
|
async getPopularMovies(page = 1) {
|
||||||
const data = await this.makeRequest('GET', '/movie/popular', { page });
|
const response = await this.makeRequest('GET', '/movie/popular', { page });
|
||||||
|
const data = response.data;
|
||||||
data.results = data.results.map(movie => ({
|
data.results = data.results.map(movie => ({
|
||||||
...movie,
|
...movie,
|
||||||
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
||||||
backdrop_path: this.getImageURL(movie.backdrop_path, 'w1280')
|
backdrop_path: this.getImageURL(movie.backdrop_path, 'original')
|
||||||
}));
|
}));
|
||||||
return data;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTopRatedMovies(page = 1) {
|
async getTopRatedMovies(page = 1) {
|
||||||
const data = await this.makeRequest('GET', '/movie/top_rated', { page });
|
const response = await this.makeRequest('GET', '/movie/top_rated', { page });
|
||||||
|
const data = response.data;
|
||||||
data.results = data.results.map(movie => ({
|
data.results = data.results.map(movie => ({
|
||||||
...movie,
|
...movie,
|
||||||
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
||||||
backdrop_path: this.getImageURL(movie.backdrop_path, 'w1280')
|
backdrop_path: this.getImageURL(movie.backdrop_path, 'original')
|
||||||
}));
|
}));
|
||||||
return data;
|
return response;
|
||||||
}
|
|
||||||
|
|
||||||
async getUpcomingMovies(page = 1) {
|
|
||||||
const data = await this.makeRequest('GET', '/movie/upcoming', { page });
|
|
||||||
data.results = data.results.map(movie => ({
|
|
||||||
...movie,
|
|
||||||
poster_path: this.getImageURL(movie.poster_path, 'w500'),
|
|
||||||
backdrop_path: this.getImageURL(movie.backdrop_path, 'w1280')
|
|
||||||
}));
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMovieExternalIDs(id) {
|
|
||||||
return await this.makeRequest('GET', `/movie/${id}/external_ids`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = TMDBClient;
|
|
||||||
|
|||||||
24
src/index.js
24
src/index.js
@@ -10,8 +10,8 @@ const app = express();
|
|||||||
const port = process.env.PORT || 3000;
|
const port = process.env.PORT || 3000;
|
||||||
|
|
||||||
// Определяем базовый URL для документации
|
// Определяем базовый URL для документации
|
||||||
const BASE_URL = process.env.VERCEL_URL
|
const BASE_URL = process.env.NODE_ENV === 'production'
|
||||||
? `https://${process.env.VERCEL_URL}`
|
? 'https://neomovies-api.vercel.app'
|
||||||
: `http://localhost:${port}`;
|
: `http://localhost:${port}`;
|
||||||
|
|
||||||
// Swagger configuration
|
// Swagger configuration
|
||||||
@@ -30,7 +30,7 @@ const swaggerOptions = {
|
|||||||
servers: [
|
servers: [
|
||||||
{
|
{
|
||||||
url: BASE_URL,
|
url: BASE_URL,
|
||||||
description: process.env.VERCEL_URL ? 'Production server' : 'Development server',
|
description: process.env.NODE_ENV === 'production' ? 'Production server' : 'Development server',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tags: [
|
tags: [
|
||||||
@@ -207,22 +207,26 @@ const swaggerDocs = swaggerJsdoc(swaggerOptions);
|
|||||||
|
|
||||||
// CORS configuration
|
// CORS configuration
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: '*',
|
origin: true, // Разрешаем все origins в development
|
||||||
|
credentials: true,
|
||||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
||||||
allowedHeaders: ['Content-Type', 'Authorization'],
|
allowedHeaders: ['X-Requested-With', 'Content-Type', 'Authorization', 'Accept']
|
||||||
credentials: true
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Handle preflight requests
|
||||||
|
app.options('*', cors());
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.static(path.join(__dirname, 'public')));
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
|
|
||||||
// TMDB client middleware
|
// TMDB client middleware
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
if (!process.env.TMDB_ACCESS_TOKEN) {
|
const token = process.env.TMDB_ACCESS_TOKEN || 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkOWRlZTY5ZjYzNzYzOGU2MjY5OGZhZGY0ZjhhYTNkYyIsInN1YiI6IjY1OTVkNmM5ODY5ZTc1NzJmOTY1MjZiZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.Wd_tBYGkAoGPVHq3A5DwV1iLs_eGvH3RRz86ghJTmU8';
|
||||||
|
if (!token) {
|
||||||
return res.status(500).json({ error: 'TMDB_ACCESS_TOKEN is not set' });
|
return res.status(500).json({ error: 'TMDB_ACCESS_TOKEN is not set' });
|
||||||
}
|
}
|
||||||
req.tmdb = new TMDBClient(process.env.TMDB_ACCESS_TOKEN);
|
req.tmdb = new TMDBClient(token);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -268,5 +272,5 @@ app.use((err, req, res, next) => {
|
|||||||
// Start server
|
// Start server
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Server is running on port ${port}`);
|
console.log(`Server is running on port ${port}`);
|
||||||
console.log(`Documentation available at https://neomovies-api/api-docs`);
|
console.log(`Documentation available at https://neomovies-api.vercel.app/api-docs`);
|
||||||
});
|
});
|
||||||
@@ -62,30 +62,34 @@ const { formatDate } = require('../utils/date');
|
|||||||
router.get('/search', async (req, res) => {
|
router.get('/search', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { query, page = 1 } = req.query;
|
const { query, page = 1 } = req.query;
|
||||||
|
|
||||||
if (!query) {
|
if (!query) {
|
||||||
return res.status(400).json({ error: "query parameter is required" });
|
return res.status(400).json({ error: 'Query parameter is required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await req.tmdb.searchMovies(query, page);
|
const response = await req.tmdb.searchMovies(query, page);
|
||||||
|
|
||||||
const response = {
|
if (!response || !response.data) {
|
||||||
page: results.page,
|
throw new Error('Failed to fetch data from TMDB');
|
||||||
total_pages: results.total_pages,
|
}
|
||||||
total_results: results.total_results,
|
|
||||||
results: results.results.map(movie => ({
|
|
||||||
id: movie.id,
|
|
||||||
title: movie.title,
|
|
||||||
overview: movie.overview,
|
|
||||||
release_date: formatDate(movie.release_date),
|
|
||||||
vote_average: movie.vote_average,
|
|
||||||
poster_path: movie.poster_path,
|
|
||||||
backdrop_path: movie.backdrop_path
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
res.json(response);
|
const { results, ...rest } = response.data;
|
||||||
|
|
||||||
|
const formattedResults = results.map(movie => ({
|
||||||
|
...movie,
|
||||||
|
release_date: formatDate(movie.release_date)
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
...rest,
|
||||||
|
results: formattedResults
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message });
|
console.error('Search movies error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
error: 'Failed to search movies',
|
||||||
|
details: process.env.NODE_ENV === 'development' ? error.message : undefined
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
35
vercel.json
35
vercel.json
@@ -9,14 +9,43 @@
|
|||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"src": "/api-docs/(.*)",
|
"src": "/api-docs/(.*)",
|
||||||
"dest": "src/index.js"
|
"headers": {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
||||||
|
"Access-Control-Allow-Headers": "X-Requested-With, Content-Type, Accept"
|
||||||
|
},
|
||||||
|
"dest": "/src/index.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/movies/(.*)",
|
||||||
|
"headers": {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
||||||
|
"Access-Control-Allow-Headers": "X-Requested-With, Content-Type, Accept"
|
||||||
|
},
|
||||||
|
"dest": "/src/index.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/health",
|
||||||
|
"headers": {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||||
|
"Access-Control-Allow-Headers": "X-Requested-With, Content-Type, Accept"
|
||||||
|
},
|
||||||
|
"dest": "/src/index.js"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/(.*)",
|
"src": "/(.*)",
|
||||||
"dest": "src/index.js"
|
"headers": {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
||||||
|
"Access-Control-Allow-Headers": "X-Requested-With, Content-Type, Accept"
|
||||||
|
},
|
||||||
|
"dest": "/src/index.js"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"NODE_ENV": "production"
|
"NODE_ENV": "production",
|
||||||
|
"TMDB_ACCESS_TOKEN": "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkOWRlZTY5ZjYzNzYzOGU2MjY5OGZhZGY0ZjhhYTNkYyIsInN1YiI6IjY1OTVkNmM5ODY5ZTc1NzJmOTY1MjZiZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.Wd_tBYGkAoGPVHq3A5DwV1iLs_eGvH3RRz86ghJTmU8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user