add video backend server, fix styling and more #54
+70
-1
@@ -122,16 +122,85 @@
|
|||||||
<th>Titre</th>
|
<th>Titre</th>
|
||||||
<th style="width: 20%;">Catégorie</th>
|
<th style="width: 20%;">Catégorie</th>
|
||||||
<th style="width: 12%;">Année</th>
|
<th style="width: 12%;">Année</th>
|
||||||
|
<th style="width: 1%;">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="admin-movies-body">
|
<tbody id="admin-movies-body">
|
||||||
<tr><td colspan="4" class="text-muted">Charge la liste…</td></tr>
|
<tr><td colspan="5" class="text-muted">Charge la liste…</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Edit movie modal -->
|
||||||
|
<div class="modal fade" id="editMovieModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editMovieModalTitle">Modifier le film</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="edit_movie_id">
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="edit_imdb_id" class="form-label">IMDB ID (ex: tt1234567)</label>
|
||||||
|
<input type="text" class="form-control" id="edit_imdb_id" placeholder="tt..." required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="edit_year" class="form-label">Oscar year</label>
|
||||||
|
<input type="number" class="form-control" id="edit_year" placeholder="2026" min="1900" max="3000">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="edit_category" class="form-label">Catégorie</label>
|
||||||
|
<input type="text" class="form-control" id="edit_category" placeholder="Best Picture, etc." required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="edit_vod_link" class="form-label">Lien VOD</label>
|
||||||
|
<input type="url" class="form-control" id="edit_vod_link" placeholder="https://..." required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="edit_refresh_omdb">
|
||||||
|
<label class="form-check-label" for="edit_refresh_omdb">
|
||||||
|
Recharger depuis OMDb (écrase titre / plot / rating / poster)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">Si coché, le serveur refait un appel OMDb via l’IMDB ID au moment de l’enregistrement.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="edit_title" class="form-label">Titre</label>
|
||||||
|
<input type="text" class="form-control" id="edit_title" placeholder="Title">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="edit_rating" class="form-label">Rating (imdbRating)</label>
|
||||||
|
<input type="text" class="form-control" id="edit_rating" placeholder="7.9">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="edit_poster" class="form-label">Poster URL</label>
|
||||||
|
<input type="url" class="form-control" id="edit_poster" placeholder="https://...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="edit_description" class="form-label">Description (Plot)</label>
|
||||||
|
<textarea class="form-control" id="edit_description" rows="4" placeholder="Plot..."></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="save-movie-changes">Enregistrer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="./js/app_control.js"></script>
|
<script src="./js/app_control.js"></script>
|
||||||
|
|
||||||
|
|||||||
+170
-11
@@ -40,6 +40,24 @@ window.onload = async function () {
|
|||||||
const selectAllBox = document.getElementById('select-all-movies');
|
const selectAllBox = document.getElementById('select-all-movies');
|
||||||
const selectedCountEl = document.getElementById('selected-count');
|
const selectedCountEl = document.getElementById('selected-count');
|
||||||
|
|
||||||
|
const editMovieModalEl = document.getElementById('editMovieModal');
|
||||||
|
const editMovieModal = editMovieModalEl ? new bootstrap.Modal(editMovieModalEl) : null;
|
||||||
|
const editMovieModalTitleEl = document.getElementById('editMovieModalTitle');
|
||||||
|
const saveMovieChangesBtn = document.getElementById('save-movie-changes');
|
||||||
|
|
||||||
|
const editMovieIdEl = document.getElementById('edit_movie_id');
|
||||||
|
const editImdbIdEl = document.getElementById('edit_imdb_id');
|
||||||
|
const editYearEl = document.getElementById('edit_year');
|
||||||
|
const editCategoryEl = document.getElementById('edit_category');
|
||||||
|
const editVodLinkEl = document.getElementById('edit_vod_link');
|
||||||
|
const editRefreshOmdbEl = document.getElementById('edit_refresh_omdb');
|
||||||
|
const editTitleEl = document.getElementById('edit_title');
|
||||||
|
const editRatingEl = document.getElementById('edit_rating');
|
||||||
|
const editPosterEl = document.getElementById('edit_poster');
|
||||||
|
const editDescriptionEl = document.getElementById('edit_description');
|
||||||
|
|
||||||
|
let moviesById = new Map();
|
||||||
|
|
||||||
function showResponse(kind, message) {
|
function showResponse(kind, message) {
|
||||||
responseEl.classList.remove('d-none', 'alert-success', 'alert-danger', 'alert-warning');
|
responseEl.classList.remove('d-none', 'alert-success', 'alert-danger', 'alert-warning');
|
||||||
responseEl.classList.add(kind === 'success' ? 'alert-success' : kind === 'warning' ? 'alert-warning' : 'alert-danger');
|
responseEl.classList.add(kind === 'success' ? 'alert-success' : kind === 'warning' ? 'alert-warning' : 'alert-danger');
|
||||||
@@ -53,6 +71,10 @@ window.onload = async function () {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidImdbId(value) {
|
||||||
|
return /^tt\d{5,}$/.test(String(value || '').trim());
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('reset-form').addEventListener('click', function () {
|
document.getElementById('reset-form').addEventListener('click', function () {
|
||||||
form.reset();
|
form.reset();
|
||||||
responseEl.classList.add('d-none');
|
responseEl.classList.add('d-none');
|
||||||
@@ -117,18 +139,24 @@ window.onload = async function () {
|
|||||||
const yearsRes = await fetch('/api/movies/years');
|
const yearsRes = await fetch('/api/movies/years');
|
||||||
const years = yearsRes.ok ? await yearsRes.json() : [];
|
const years = yearsRes.ok ? await yearsRes.json() : [];
|
||||||
|
|
||||||
// Populate both the add form (default) and manage dropdown
|
// Rebuild manage dropdown from scratch (so removed years disappear)
|
||||||
const existingValues = new Set([...manageYearSelect.options].map((o) => o.value));
|
const previousSelection = manageYearSelect.value;
|
||||||
|
manageYearSelect.innerHTML = '';
|
||||||
|
const allOpt = document.createElement('option');
|
||||||
|
allOpt.value = '';
|
||||||
|
allOpt.textContent = 'Toutes';
|
||||||
|
manageYearSelect.appendChild(allOpt);
|
||||||
|
|
||||||
years.forEach((y) => {
|
years.forEach((y) => {
|
||||||
const value = String(y);
|
|
||||||
if (!existingValues.has(value)) {
|
|
||||||
const opt = document.createElement('option');
|
const opt = document.createElement('option');
|
||||||
opt.value = value;
|
opt.value = String(y);
|
||||||
opt.textContent = value;
|
opt.textContent = String(y);
|
||||||
manageYearSelect.appendChild(opt);
|
manageYearSelect.appendChild(opt);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const stillExists = years.map(String).includes(previousSelection);
|
||||||
|
manageYearSelect.value = stillExists ? previousSelection : '';
|
||||||
|
|
||||||
// Default add-year to latest year if empty
|
// Default add-year to latest year if empty
|
||||||
if (years.length > 0 && !yearInput.value) {
|
if (years.length > 0 && !yearInput.value) {
|
||||||
yearInput.value = String(years[0]);
|
yearInput.value = String(years[0]);
|
||||||
@@ -155,22 +183,140 @@ window.onload = async function () {
|
|||||||
selectAllBox.checked = allBoxes.length > 0 && checked === allBoxes.length;
|
selectAllBox.checked = allBoxes.length > 0 && checked === allBoxes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openEditModal(movieId) {
|
||||||
|
if (!editMovieModal) return;
|
||||||
|
const movie = moviesById.get(movieId);
|
||||||
|
if (!movie) return;
|
||||||
|
|
||||||
|
editMovieIdEl.value = movie._id || '';
|
||||||
|
editImdbIdEl.value = movie.imdb_id || '';
|
||||||
|
editYearEl.value = movie.year ? String(movie.year) : '';
|
||||||
|
editCategoryEl.value = movie.category || '';
|
||||||
|
editVodLinkEl.value = movie.vod_link || '';
|
||||||
|
editRefreshOmdbEl.checked = false;
|
||||||
|
|
||||||
|
editTitleEl.value = movie.title || '';
|
||||||
|
editRatingEl.value = movie.rating || '';
|
||||||
|
editPosterEl.value = movie.poster || '';
|
||||||
|
editDescriptionEl.value = movie.description || '';
|
||||||
|
|
||||||
|
if (editMovieModalTitleEl) {
|
||||||
|
const label = movie.title ? `Modifier: ${movie.title}` : 'Modifier le film';
|
||||||
|
editMovieModalTitleEl.textContent = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
editMovieModal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveMovieChanges() {
|
||||||
|
const movieId = (editMovieIdEl.value || '').trim();
|
||||||
|
if (!movieId) return;
|
||||||
|
|
||||||
|
const imdb_id = (editImdbIdEl.value || '').trim();
|
||||||
|
const yearRaw = (editYearEl.value || '').trim();
|
||||||
|
const category = (editCategoryEl.value || '').trim();
|
||||||
|
const vod_link = (editVodLinkEl.value || '').trim();
|
||||||
|
const refreshOmdb = !!editRefreshOmdbEl.checked;
|
||||||
|
|
||||||
|
const title = editTitleEl.value;
|
||||||
|
const rating = editRatingEl.value;
|
||||||
|
const poster = editPosterEl.value;
|
||||||
|
const description = editDescriptionEl.value;
|
||||||
|
|
||||||
|
if (!isValidImdbId(imdb_id)) {
|
||||||
|
showResponse('warning', 'IMDB ID invalide. Exemple attendu: tt1234567');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let year = null;
|
||||||
|
if (yearRaw !== '') {
|
||||||
|
year = parseYear(yearRaw);
|
||||||
|
if (!year) {
|
||||||
|
showResponse('warning', 'Année invalide. Exemple attendu: 2026');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!category) {
|
||||||
|
showResponse('warning', 'Catégorie invalide.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vod_link) {
|
||||||
|
showResponse('warning', 'Lien VOD invalide.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
imdb_id,
|
||||||
|
year: yearRaw === '' ? null : year,
|
||||||
|
category,
|
||||||
|
vod_link,
|
||||||
|
refreshOmdb
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only send manual fields if we're not refreshing from OMDb
|
||||||
|
if (!refreshOmdb) {
|
||||||
|
body.title = title;
|
||||||
|
body.description = description;
|
||||||
|
body.rating = rating;
|
||||||
|
body.poster = poster;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveMovieChangesBtn.disabled = true;
|
||||||
|
saveMovieChangesBtn.textContent = 'Enregistrement...';
|
||||||
|
|
||||||
|
const res = await fetch(`/api/movies/${encodeURIComponent(movieId)}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
let msg = `Erreur (${res.status})`;
|
||||||
|
try {
|
||||||
|
const data = await res.json();
|
||||||
|
msg = data.message || data.error || msg;
|
||||||
|
} catch (_) {}
|
||||||
|
showResponse('danger', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await res.json();
|
||||||
|
moviesById.set(updated._id, updated);
|
||||||
|
showResponse('success', 'Film mis à jour avec succès.');
|
||||||
|
editMovieModal.hide();
|
||||||
|
await refreshYears();
|
||||||
|
await loadMoviesForManagement();
|
||||||
|
} catch (err) {
|
||||||
|
showResponse('danger', err.message || 'Erreur réseau');
|
||||||
|
} finally {
|
||||||
|
saveMovieChangesBtn.disabled = false;
|
||||||
|
saveMovieChangesBtn.textContent = 'Enregistrer';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadMoviesForManagement() {
|
async function loadMoviesForManagement() {
|
||||||
const year = manageYearSelect.value;
|
const year = manageYearSelect.value;
|
||||||
const url = year ? `/api/movies?year=${encodeURIComponent(year)}` : '/api/movies';
|
const url = year ? `/api/movies?year=${encodeURIComponent(year)}` : '/api/movies';
|
||||||
adminMoviesBody.innerHTML = `<tr><td colspan="4" class="text-muted">Chargement…</td></tr>`;
|
adminMoviesBody.innerHTML = `<tr><td colspan="5" class="text-muted">Chargement…</td></tr>`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url, { method: 'GET' });
|
const res = await fetch(url, { method: 'GET' });
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
adminMoviesBody.innerHTML = `<tr><td colspan="4" class="text-danger">Erreur lors du chargement (${res.status})</td></tr>`;
|
adminMoviesBody.innerHTML = `<tr><td colspan="5" class="text-danger">Erreur lors du chargement (${res.status})</td></tr>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const movies = await res.json();
|
const movies = await res.json();
|
||||||
movies.sort((a, b) => (a.title || '').localeCompare((b.title || ''), 'fr', { sensitivity: 'base' }));
|
movies.sort((a, b) => (a.title || '').localeCompare((b.title || ''), 'fr', { sensitivity: 'base' }));
|
||||||
|
moviesById = new Map(movies.map((m) => [m._id, m]));
|
||||||
|
|
||||||
if (!movies.length) {
|
if (!movies.length) {
|
||||||
adminMoviesBody.innerHTML = `<tr><td colspan="4" class="text-muted">Aucun film.</td></tr>`;
|
adminMoviesBody.innerHTML = `<tr><td colspan="5" class="text-muted">Aucun film.</td></tr>`;
|
||||||
updateSelectionUI();
|
updateSelectionUI();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -188,6 +334,11 @@ window.onload = async function () {
|
|||||||
</td>
|
</td>
|
||||||
<td>${m.category || ''}</td>
|
<td>${m.category || ''}</td>
|
||||||
<td>${m.year || ''}</td>
|
<td>${m.year || ''}</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary" data-edit-movie-id="${m._id}">
|
||||||
|
Éditer
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
`;
|
`;
|
||||||
adminMoviesBody.appendChild(tr);
|
adminMoviesBody.appendChild(tr);
|
||||||
});
|
});
|
||||||
@@ -196,9 +347,13 @@ window.onload = async function () {
|
|||||||
cb.addEventListener('change', updateSelectionUI);
|
cb.addEventListener('change', updateSelectionUI);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
adminMoviesBody.querySelectorAll('button[data-edit-movie-id]').forEach((btn) => {
|
||||||
|
btn.addEventListener('click', () => openEditModal(btn.getAttribute('data-edit-movie-id')));
|
||||||
|
});
|
||||||
|
|
||||||
updateSelectionUI();
|
updateSelectionUI();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
adminMoviesBody.innerHTML = `<tr><td colspan="4" class="text-danger">${err.message || 'Erreur réseau'}</td></tr>`;
|
adminMoviesBody.innerHTML = `<tr><td colspan="5" class="text-danger">${err.message || 'Erreur réseau'}</td></tr>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +404,10 @@ window.onload = async function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (saveMovieChangesBtn) {
|
||||||
|
saveMovieChangesBtn.addEventListener('click', saveMovieChanges);
|
||||||
|
}
|
||||||
|
|
||||||
// Initial load
|
// Initial load
|
||||||
await refreshYears();
|
await refreshYears();
|
||||||
await loadMoviesForManagement();
|
await loadMoviesForManagement();
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ function parseOscarYear(raw) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidImdbId(value) {
|
||||||
|
return typeof value === "string" && /^tt\d{5,}$/.test(value.trim());
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch movie details from OMDb API
|
// Fetch movie details from OMDb API
|
||||||
async function fetchMovieDetailsFromOmdb(imdb_id) {
|
async function fetchMovieDetailsFromOmdb(imdb_id) {
|
||||||
const apiKey = process.env.OMDB_API; // Replace with your actual OMDb API key
|
const apiKey = process.env.OMDB_API; // Replace with your actual OMDb API key
|
||||||
@@ -233,4 +237,85 @@ router.delete("/delete", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Admin: update a movie (edit fields, optionally refresh from OMDb)
|
||||||
|
router.put("/:id", async (req, res) => {
|
||||||
|
const token = req.headers.authorization?.split(" ")[1];
|
||||||
|
if (!token) return res.status(401).json({ message: "Authentication token is required" });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
|
if (!decoded.admin) {
|
||||||
|
return res.status(403).json({ message: "You do not have admin privileges" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = req.params;
|
||||||
|
const {
|
||||||
|
imdb_id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
rating,
|
||||||
|
poster,
|
||||||
|
year,
|
||||||
|
category,
|
||||||
|
vod_link,
|
||||||
|
refreshOmdb,
|
||||||
|
} = req.body || {};
|
||||||
|
|
||||||
|
if (imdb_id !== undefined && imdb_id !== null && !isValidImdbId(imdb_id)) {
|
||||||
|
return res.status(400).json({ message: "IMDB ID invalide. Exemple attendu: tt1234567" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (year !== undefined && year !== null && year !== "") {
|
||||||
|
const parsedYear = parseOscarYear(year);
|
||||||
|
if (!parsedYear) {
|
||||||
|
return res.status(400).json({ message: "Année invalide (ex: 2026)" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof category === "string" && category.trim() === "") {
|
||||||
|
return res.status(400).json({ message: "Catégorie invalide." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof vod_link === "string" && vod_link.trim() === "") {
|
||||||
|
return res.status(400).json({ message: "Lien VOD invalide." });
|
||||||
|
}
|
||||||
|
|
||||||
|
const movie = await Movie.findById(id);
|
||||||
|
if (!movie) return res.status(404).json({ message: "Movie not found" });
|
||||||
|
|
||||||
|
if (imdb_id !== undefined) movie.imdb_id = String(imdb_id).trim();
|
||||||
|
if (category !== undefined) movie.category = String(category).trim();
|
||||||
|
if (vod_link !== undefined) movie.vod_link = String(vod_link).trim();
|
||||||
|
|
||||||
|
if (year !== undefined && year !== null && year !== "") {
|
||||||
|
movie.year = parseOscarYear(year);
|
||||||
|
} else if (year === null || year === "") {
|
||||||
|
// allow clearing the year explicitly
|
||||||
|
movie.year = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If refreshOmdb is true, overwrite OMDb-derived fields from the imdb_id
|
||||||
|
if (refreshOmdb) {
|
||||||
|
const details = await fetchMovieDetailsFromOmdb(movie.imdb_id);
|
||||||
|
movie.title = details.title;
|
||||||
|
movie.description = details.description;
|
||||||
|
movie.rating = details.rating;
|
||||||
|
movie.poster = details.poster;
|
||||||
|
} else {
|
||||||
|
if (title !== undefined) movie.title = title;
|
||||||
|
if (description !== undefined) movie.description = description;
|
||||||
|
if (rating !== undefined) movie.rating = rating;
|
||||||
|
if (poster !== undefined) movie.poster = poster;
|
||||||
|
}
|
||||||
|
|
||||||
|
await movie.save();
|
||||||
|
return res.status(200).json(movie);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof jwt.JsonWebTokenError) {
|
||||||
|
return res.status(401).json({ message: "Invalid or expired token" });
|
||||||
|
}
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user