Update 4 files

- /src/index.js
- /src/routes/movies.js
- /src/config/tmdb.js
- /vercel.json
This commit is contained in:
2025-01-03 20:08:13 +00:00
parent 0c1cfb1ac5
commit 8b11f89347
4 changed files with 106 additions and 69 deletions

View File

@@ -7,13 +7,27 @@ class TMDBClient {
headers: {
'Authorization': `Bearer ${accessToken}`,
'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 = {}) {
try {
const response = await this.client.request({
const response = await this.client({
method,
url: endpoint,
params: {
@@ -22,10 +36,12 @@ class TMDBClient {
region: 'RU'
}
});
return response.data;
return response;
} catch (error) {
console.error(`TMDB API Error: ${error.message}`);
throw error;
if (error.response) {
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) {
const data = await this.makeRequest('GET', '/search/movie', {
const response = await this.makeRequest('GET', '/search/movie', {
query,
page,
include_adult: false
});
const data = response.data;
// Фильтруем результаты
data.results = data.results.filter(movie =>
movie.poster_path &&
movie.overview &&
movie.vote_average > 0
);
// Добавляем полные URL для изображений
data.results = data.results.map(movie => ({
).map(movie => ({
...movie,
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) {
const movie = await this.makeRequest('GET', `/movie/${id}`);
const response = await this.makeRequest('GET', `/movie/${id}`);
const movie = response.data;
return {
...movie,
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) {
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 => ({
...movie,
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) {
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 => ({
...movie,
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;
}
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;
return response;

View File

@@ -10,8 +10,8 @@ const app = express();
const port = process.env.PORT || 3000;
// Определяем базовый URL для документации
const BASE_URL = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
const BASE_URL = process.env.NODE_ENV === 'production'
? 'https://neomovies-api.vercel.app'
: `http://localhost:${port}`;
// Swagger configuration
@@ -30,7 +30,7 @@ const swaggerOptions = {
servers: [
{
url: BASE_URL,
description: process.env.VERCEL_URL ? 'Production server' : 'Development server',
description: process.env.NODE_ENV === 'production' ? 'Production server' : 'Development server',
},
],
tags: [
@@ -207,22 +207,26 @@ const swaggerDocs = swaggerJsdoc(swaggerOptions);
// CORS configuration
app.use(cors({
origin: '*',
origin: true, // Разрешаем все origins в development
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
allowedHeaders: ['X-Requested-With', 'Content-Type', 'Authorization', 'Accept']
}));
// Handle preflight requests
app.options('*', cors());
// Middleware
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
// TMDB client middleware
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' });
}
req.tmdb = new TMDBClient(process.env.TMDB_ACCESS_TOKEN);
req.tmdb = new TMDBClient(token);
next();
});
@@ -268,5 +272,5 @@ app.use((err, req, res, next) => {
// Start server
app.listen(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`);
});

View File

@@ -62,30 +62,34 @@ const { formatDate } = require('../utils/date');
router.get('/search', async (req, res) => {
try {
const { query, page = 1 } = req.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 = {
page: results.page,
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
}))
};
if (!response || !response.data) {
throw new Error('Failed to fetch data from TMDB');
}
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) {
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
});
}
});

View File

@@ -9,14 +9,43 @@
"routes": [
{
"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": "/(.*)",
"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": {
"NODE_ENV": "production"
"NODE_ENV": "production",
"TMDB_ACCESS_TOKEN": "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkOWRlZTY5ZjYzNzYzOGU2MjY5OGZhZGY0ZjhhYTNkYyIsInN1YiI6IjY1OTVkNmM5ODY5ZTc1NzJmOTY1MjZiZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.Wd_tBYGkAoGPVHq3A5DwV1iLs_eGvH3RRz86ghJTmU8"
}
}