diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d56a18c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.specstory
\ No newline at end of file
diff --git a/autopost/config.js b/autopost/config.js
index eca4207..8d927d4 100644
--- a/autopost/config.js
+++ b/autopost/config.js
@@ -36,5 +36,9 @@ module.exports = {
auth: {
username: 'user',
password: 'pass'
- }
+ },
+
+ // Configuration pour le renvoi vers le site principal
+ apiUrl: 'A NOUS DEMANDER SUR DISCORD',
+ apiKey: 'A RETROUVER DANS VOTRE PROFIL'
};
\ No newline at end of file
diff --git a/autopost/posteur.sh b/autopost/posteur.sh
index 73378ff..5c484cf 100644
--- a/autopost/posteur.sh
+++ b/autopost/posteur.sh
@@ -78,11 +78,25 @@ while true; do
echo -e "${JAUNE}VERIF DU NZB${NORMAL}"
nzbsizebit=$(bash ${ANALYZER} "${DOSSIER_NZB_ATTENTE}${FILESANSEXT}.nzb" | jq '.Taillebit')
echo -e "NZB_SIZE : ${nzbsizebit}"
+ # Détermine si on est en mode "symlink" selon la conf
+ symlink_mode=0
+ case "${MOVE_CMD:-}" in
+ "cp -rs"|"ln -s") symlink_mode=1 ;;
+ esac
+
if [[ "${name}" =~ \.(iso)$ ]]; then
- jsonsizebit=$(du -b -c "${name}" | grep total | awk '{ print $1 }')
+ if (( symlink_mode )); then
+ # Suivre le lien symbolique pour obtenir la taille réelle
+ jsonsizebit=$(du -Lb -c -- "${name}" | awk '/total/ {print $1}')
+ else
+ # Comportement inchangé
+ jsonsizebit=$(du -b -c -- "${name}" | awk '/total/ {print $1}')
+ fi
else
+ # Comportement inchangé
jsonsizebit=$(jq -r '.media.track[] | select(."@type" == "General") | .FileSize' "${DOSSIER_NFO}${FILESANSEXT}.json")
fi
+
echo -e "MEDIAINFO_SIZE : ${jsonsizebit}"
if [[ ${nzbsizebit} -le ${jsonsizebit} ]] || [[ ${nzbsizebit} = "NAN" ]]; then
diff --git a/autopost/public/autopost.js b/autopost/public/autopost.js
index 7f55eef..76406c5 100644
--- a/autopost/public/autopost.js
+++ b/autopost/public/autopost.js
@@ -58,6 +58,10 @@ function updateTable(rows) {
? ' | DL'
: '';
+ var resendLink = (parseInt(row.status) === 1)
+ ? ' | Renvoyer'
+ : '';
+
var tr =
'
' +
'| ' + esc(row.nom) + ' | ' +
@@ -66,7 +70,7 @@ function updateTable(rows) {
'' +
'Editer | ' +
'Supprimer' +
- logLink + mediainfoLink + dlLink +
+ logLink + mediainfoLink + dlLink + resendLink +
' | ' +
'
';
@@ -261,6 +265,25 @@ $(document).ready(function() {
pendingDeleteId = null;
}
+ // Fonctions pour la modal de renvoi
+ var pendingResendId = null;
+ var pendingResendFilename = null;
+
+ function openConfirmResendModal(id, filename) {
+ pendingResendId = id;
+ pendingResendFilename = filename;
+ $('#confirmResendItemName').text(filename || ('ID ' + id));
+ $('#confirmResendModal').removeClass('hidden').fadeIn(120);
+ }
+
+ function closeConfirmResendModal() {
+ $('#confirmResendModal').fadeOut(120, function() {
+ $(this).addClass('hidden');
+ });
+ pendingResendId = null;
+ pendingResendFilename = null;
+ }
+
function showToast(msg) {
$('#toastMsg').text(msg || 'Opération effectuée');
$('#toast').removeClass('hidden').fadeIn(120);
@@ -282,17 +305,33 @@ $(document).ready(function() {
closeConfirmModal();
});
- // Clic sur l’overlay pour fermer
+ // Clic sur l'overlay pour fermer
$('#confirmDeleteModal').on('click', function(e) {
if (e.target === this) { closeConfirmModal(); }
});
+ // Gestionnaires pour la modal de renvoi
+ $('#cancelResendBtn, #confirmResendClose').on('click', function() {
+ closeConfirmResendModal();
+ });
+
+ // Clic sur l'overlay pour fermer la modal de renvoi
+ $('#confirmResendModal').on('click', function(e) {
+ if (e.target === this) { closeConfirmResendModal(); }
+ });
+
// Accessibilité clavier: Esc = fermer, Enter = confirmer
$(document).on('keydown', function(e) {
- var modalVisible = !$('#confirmDeleteModal').hasClass('hidden');
- if (!modalVisible) return;
- if (e.key === 'Escape') { closeConfirmModal(); }
- if (e.key === 'Enter') { $('#confirmDeleteBtn').click(); }
+ var deleteModalVisible = !$('#confirmDeleteModal').hasClass('hidden');
+ var resendModalVisible = !$('#confirmResendModal').hasClass('hidden');
+
+ if (deleteModalVisible) {
+ if (e.key === 'Escape') { closeConfirmModal(); }
+ if (e.key === 'Enter') { $('#confirmDeleteBtn').click(); }
+ } else if (resendModalVisible) {
+ if (e.key === 'Escape') { closeConfirmResendModal(); }
+ if (e.key === 'Enter') { $('#confirmResendBtn').click(); }
+ }
});
// Confirmer et supprimer via AJAX
@@ -318,6 +357,38 @@ $(document).ready(function() {
});
});
+ // Confirmer et renvoyer via AJAX
+ $('#confirmResendBtn').on('click', function() {
+ if (!pendingResendId) return;
+ var releaseId = pendingResendId;
+ var filename = pendingResendFilename;
+
+ var $button = $(this);
+ var originalText = $button.text();
+ $button.text('Envoi...');
+
+ $.ajax({
+ url: '/autopost/resend/' + releaseId,
+ type: 'POST',
+ success: function(data) {
+ showToast(data.message || 'Renvoi effectué avec succès');
+ console.log('Renvoi réussi pour', filename);
+ },
+ error: function(xhr) {
+ var errorMsg = 'Erreur lors du renvoi';
+ if (xhr.responseJSON && xhr.responseJSON.error) {
+ errorMsg = xhr.responseJSON.error;
+ }
+ alert(errorMsg);
+ console.error('Erreur renvoi:', xhr.responseJSON);
+ },
+ complete: function() {
+ $button.text(originalText);
+ closeConfirmResendModal();
+ }
+ });
+ });
+
// Affichage log
$(document).on('click', '.log-link', function(e) {
e.preventDefault();
@@ -419,4 +490,219 @@ $(document).ready(function() {
alert('Erreur lors de la copie : ' + err);
});
});
+
+ // Renvoi vers le site principal
+ $(document).on('click', '.resend-link', function(e) {
+ e.preventDefault();
+ var releaseId = $(this).data('id');
+ var filename = $(this).data('filename');
+ openConfirmResendModal(releaseId, filename);
+ });
+
+ // ==================== GESTION SÉLECTION MULTIPLE ====================
+
+ function updateBulkActions() {
+ const selectedCheckboxes = $('.row-checkbox:checked');
+ const count = selectedCheckboxes.length;
+
+ if (count > 0) {
+ $('#bulkActions').removeClass('hidden');
+ $('#selectedCount').text(count + ' élément' + (count > 1 ? 's' : '') + ' sélectionné' + (count > 1 ? 's' : ''));
+ } else {
+ $('#bulkActions').addClass('hidden');
+ }
+
+ // Mettre à jour la checkbox "Tout sélectionner"
+ const totalCheckboxes = $('.row-checkbox').length;
+ const allChecked = count === totalCheckboxes && totalCheckboxes > 0;
+ const someChecked = count > 0 && count < totalCheckboxes;
+
+ $('#selectAll').prop('checked', allChecked);
+ $('#selectAll').prop('indeterminate', someChecked);
+ }
+
+ // Checkbox "Tout sélectionner"
+ $(document).on('click', '#selectAll', function() {
+ const isChecked = $(this).is(':checked');
+ $('.row-checkbox').prop('checked', isChecked);
+ updateBulkActions();
+ });
+
+ // Checkboxes individuelles
+ $(document).on('click', '.row-checkbox', function() {
+ updateBulkActions();
+ });
+
+ // Bouton "Éditer la sélection"
+ $(document).on('click', '#bulkEditBtn', function() {
+ const selectedCheckboxes = $('.row-checkbox:checked');
+ const count = selectedCheckboxes.length;
+
+ if (count === 0) return;
+
+ $('#bulkEditCount').text(count);
+ $('#bulkEditModal').removeClass('hidden').fadeIn(120);
+ });
+
+ // Bouton "Supprimer la sélection"
+ $(document).on('click', '#bulkDeleteBtn', function() {
+ const selectedCheckboxes = $('.row-checkbox:checked');
+ const count = selectedCheckboxes.length;
+
+ if (count === 0) return;
+
+ // Construire la liste des éléments à supprimer
+ let listHTML = '';
+ selectedCheckboxes.each(function() {
+ const name = $(this).data('name');
+ const id = $(this).data('id');
+ listHTML += `• ${name} (ID: ${id})
`;
+ });
+
+ $('#bulkDeleteCount').text(count);
+ $('#bulkDeleteList').html(listHTML);
+ $('#bulkDeleteModal').removeClass('hidden').fadeIn(120);
+ });
+
+ // Fermeture des modals en lot
+ $('#bulkEditClose, #bulkDeleteClose, #cancelBulkDeleteBtn').on('click', function() {
+ $('#bulkEditModal, #bulkDeleteModal').fadeOut(120, function() {
+ $(this).addClass('hidden');
+ });
+ });
+
+ // Clic sur l'overlay pour fermer les modals en lot
+ $('#bulkEditModal, #bulkDeleteModal').on('click', function(e) {
+ if (e.target === this) {
+ $(this).fadeOut(120, function() {
+ $(this).addClass('hidden');
+ });
+ }
+ });
+
+ // Soumission du formulaire d'édition en lot
+ $('#bulkEditForm').on('submit', function(e) {
+ e.preventDefault();
+
+ const selectedCheckboxes = $('.row-checkbox:checked');
+ const newStatus = $('#bulkStatusSelect').val();
+ const ids = [];
+
+ selectedCheckboxes.each(function() {
+ ids.push($(this).data('id'));
+ });
+
+ if (ids.length === 0) return;
+
+ const $submitBtn = $('#bulkEditForm button[type="submit"]');
+ const originalText = $submitBtn.text();
+ $submitBtn.text('Mise à jour...').prop('disabled', true);
+
+ $.ajax({
+ url: '/autopost/bulk-edit',
+ type: 'POST',
+ data: {
+ ids: ids,
+ status: newStatus,
+ _csrf: window.__BOOTSTRAP__.csrf
+ },
+ success: function(data) {
+ // Mettre à jour l'interface pour chaque ligne modifiée
+ ids.forEach(function(id) {
+ updateRowStatus(id, newStatus);
+ });
+
+ showToast(`${ids.length} élément(s) mis à jour`);
+
+ // Fermer la modal et désélectionner
+ $('#bulkEditModal').fadeOut(120, function() {
+ $(this).addClass('hidden');
+ });
+ $('.row-checkbox').prop('checked', false);
+ updateBulkActions();
+ },
+ error: function(xhr) {
+ let errorMsg = 'Erreur lors de la mise à jour en lot';
+ if (xhr.responseJSON && xhr.responseJSON.error) {
+ errorMsg = xhr.responseJSON.error;
+ }
+ alert(errorMsg);
+ },
+ complete: function() {
+ $submitBtn.text(originalText).prop('disabled', false);
+ }
+ });
+ });
+
+ // Confirmation de suppression en lot
+ $('#confirmBulkDeleteBtn').on('click', function() {
+ const selectedCheckboxes = $('.row-checkbox:checked');
+ const ids = [];
+
+ selectedCheckboxes.each(function() {
+ ids.push($(this).data('id'));
+ });
+
+ if (ids.length === 0) return;
+
+ const $button = $(this);
+ const originalText = $button.text();
+ $button.text('Suppression...').prop('disabled', true);
+
+ $.ajax({
+ url: '/autopost/bulk-delete',
+ type: 'POST',
+ data: {
+ ids: ids,
+ _csrf: window.__BOOTSTRAP__.csrf
+ },
+ success: function(data) {
+ // Supprimer visuellement les lignes
+ ids.forEach(function(id) {
+ $('#row-' + id)
+ .css('outline', '2px solid rgba(239,68,68,0.6)')
+ .fadeOut('300', function(){ $(this).remove(); });
+ });
+
+ showToast(`${ids.length} élément(s) supprimé(s)`);
+
+ // Fermer la modal
+ $('#bulkDeleteModal').fadeOut(120, function() {
+ $(this).addClass('hidden');
+ });
+ updateBulkActions();
+ },
+ error: function(xhr) {
+ let errorMsg = 'Erreur lors de la suppression en lot';
+ if (xhr.responseJSON && xhr.responseJSON.error) {
+ errorMsg = xhr.responseJSON.error;
+ }
+ alert(errorMsg);
+ },
+ complete: function() {
+ $button.text(originalText).prop('disabled', false);
+ }
+ });
+ });
+
+ // Fonction utilitaire pour mettre à jour le statut d'une ligne
+ function updateRowStatus(id, newStatus) {
+ const $row = $('#row-' + id);
+ const $statusCell = $row.find('.status-text');
+
+ let statusText = '';
+ let statusClass = '';
+ switch (parseInt(newStatus)) {
+ case 0: statusText = 'EN ATTENTE'; statusClass = 'bg-cyan-500 text-black font-bold'; break;
+ case 1: statusText = 'ENVOI TERMINÉ'; statusClass = 'bg-green-300 text-black font-bold'; break;
+ case 2: statusText = 'ERREUR'; statusClass = 'bg-red-300 text-black font-bold'; break;
+ case 3: statusText = 'DEJA DISPONIBLE'; statusClass = 'bg-pink-300 text-black font-bold'; break;
+ case 4: statusText = 'EN COURS'; statusClass = 'bg-yellow-300 text-black font-bold'; break;
+ default: statusText = 'INCONNU';
+ }
+
+ $statusCell.removeClass().addClass('px-4 py-2 border border-gray-700 status-text whitespace-nowrap ' + statusClass);
+ $statusCell.text(statusText);
+ $row.attr('data-status', newStatus);
+ }
});
diff --git a/autopost/server.js b/autopost/server.js
index f85786f..99546a7 100644
--- a/autopost/server.js
+++ b/autopost/server.js
@@ -84,16 +84,22 @@ function renderRow(row) {
const dlLink = (parseInt(row.status) === 1)
? ' | DL'
: '';
+ const resendLink = (parseInt(row.status) === 1)
+ ? ' | Renvoyer'
+ : '';
return `
+ |
+
+ |
${esc(row.nom)} |
${statusText} |
${row.id} |
Editer |
Supprimer
- ${logLink}${mediainfoLink}${dlLink}
+ ${logLink}${mediainfoLink}${dlLink}${resendLink}
|
`;
}
@@ -207,14 +213,66 @@ autopostRouter.get('/login', (req, res) => {
`);
});
-autopostRouter.post('/login', (req, res) => {
- const { username, password } = req.body;
- if (username === config.auth.username && password === config.auth.password) {
+autopostRouter.post('/login', async (req, res) => {
+ const { username, password } = req.body;
+
+ console.log(`[LOGIN] Tentative de connexion pour "${username}"`);
+ console.log(`[LOGIN] Password reçu: longueur ${password ? password.length : 0} caractères`);
+
+ // 1. Essayer d'abord l'authentification via la base de données
+ try {
+ console.log(`[LOGIN] Tentative d'authentification DB pour "${username}"`);
+ const [rows] = await db.query(
+ 'SELECT * FROM core_members WHERE name = ? AND member_group_id IN (1,2) LIMIT 1',
+ [username]
+ );
+
+ console.log(`[LOGIN] Nombre de résultats DB: ${rows.length}`);
+
+ if (rows.length > 0) {
+ const member = rows[0];
+ console.log(`[LOGIN] Utilisateur trouvé en DB:`, {
+ member_id: member.member_id,
+ name: member.name,
+ group: member.member_group_id
+ });
+
+ // Vérification du hash Argon2id
+ console.log(`[LOGIN] Vérification du mot de passe avec Argon2id`);
+ const valid = await argon2.verify(member.members_pass_hash, password);
+ console.log(`[LOGIN] Résultat vérification Argon2id: ${valid}`);
+
+ if (valid) {
+ console.log(`[LOGIN] ✅ Authentification DB réussie pour "${member.name}"`);
req.session.authenticated = true;
- res.redirect('/autopost');
+ req.session.user_id = member.member_id;
+ req.session.user_name = member.name;
+ return res.redirect('/autopost');
+ } else {
+ console.warn(`[LOGIN] ❌ Mot de passe DB incorrect pour "${member.name}"`);
+ }
} else {
- res.send('Identifiants invalides. Réessayer');
+ console.log(`[LOGIN] Aucun utilisateur trouvé en DB pour "${username}"`);
}
+ } catch (err) {
+ console.error(`[LOGIN] Erreur DB:`, err);
+ // Continue vers le fallback config
+ }
+
+ // 2. Fallback : authentification via fichier config
+ console.log(`[LOGIN] Tentative d'authentification config pour "${username}"`);
+ if (username === config.auth.username && password === config.auth.password) {
+ console.log(`[LOGIN] ✅ Authentification config réussie pour "${username}"`);
+ req.session.authenticated = true;
+ req.session.user_name = username;
+ return res.redirect('/autopost');
+ } else {
+ console.log(`[LOGIN] ❌ Authentification config échouée pour "${username}"`);
+ }
+
+ // 3. Échec des deux méthodes
+ console.warn(`[LOGIN] 🚫 Échec total d'authentification pour "${username}"`);
+ res.redirect('login?e=1');
});
autopostRouter.get('/logout', (req, res) => {
@@ -607,6 +665,184 @@ autopostRouter.post('/delete/:id', async (req, res) => {
}
});
+// --------------------------- Opérations en lot -----------------------------
+
+// Édition en lot
+autopostRouter.post('/bulk-edit', async (req, res) => {
+ const { ids, status } = req.body;
+
+ if (!Array.isArray(ids) || ids.length === 0) {
+ return res.status(400).json({ error: 'Liste d\'IDs invalide' });
+ }
+
+ const statusInt = parseInt(status, 10);
+ if (![0, 1, 2, 3, 4].includes(statusInt)) {
+ return res.status(400).json({ error: 'Statut invalide' });
+ }
+
+ // Valider que tous les IDs sont des entiers positifs
+ const validIds = ids.filter(id => parseInt(id, 10) > 0).map(id => parseInt(id, 10));
+ if (validIds.length === 0) {
+ return res.status(400).json({ error: 'Aucun ID valide' });
+ }
+
+ try {
+ const placeholders = validIds.map(() => '?').join(',');
+ const query = `UPDATE \`${config.DB_TABLE}\` SET status = ? WHERE id IN (${placeholders})`;
+ const params = [statusInt, ...validIds];
+
+ const [result] = await db.query(query, params);
+
+ res.json({
+ message: `${result.affectedRows} élément(s) mis à jour`,
+ updated: result.affectedRows
+ });
+ } catch (err) {
+ console.error(err.message);
+ res.status(500).json({ error: 'Erreur DB lors de la mise à jour en lot' });
+ }
+});
+
+// Suppression en lot
+autopostRouter.post('/bulk-delete', async (req, res) => {
+ const { ids } = req.body;
+
+ if (!Array.isArray(ids) || ids.length === 0) {
+ return res.status(400).json({ error: 'Liste d\'IDs invalide' });
+ }
+
+ // Valider que tous les IDs sont des entiers positifs
+ const validIds = ids.filter(id => parseInt(id, 10) > 0).map(id => parseInt(id, 10));
+ if (validIds.length === 0) {
+ return res.status(400).json({ error: 'Aucun ID valide' });
+ }
+
+ try {
+ const placeholders = validIds.map(() => '?').join(',');
+ const query = `DELETE FROM \`${config.DB_TABLE}\` WHERE id IN (${placeholders})`;
+
+ const [result] = await db.query(query, validIds);
+
+ res.json({
+ message: `${result.affectedRows} élément(s) supprimé(s)`,
+ deleted: result.affectedRows
+ });
+ } catch (err) {
+ console.error(err.message);
+ res.status(500).json({ error: 'Erreur DB lors de la suppression en lot' });
+ }
+});
+
+// --------------------------- Renvoi -----------------------------
+autopostRouter.post('/resend/:id', async (req, res) => {
+ const id = req.params.id;
+
+ try {
+ // Récupérer les informations de l'enregistrement
+ const [rows] = await db.query(`SELECT nom, status FROM \`${config.DB_TABLE}\` WHERE id = ?`, [id]);
+
+ if (rows.length === 0) {
+ return res.status(404).json({ error: "Enregistrement non trouvé." });
+ }
+
+ const row = rows[0];
+ const fileName = row.nom;
+ const status = parseInt(row.status);
+
+ // Vérifier que le statut est "ENVOI TERMINÉ" (status = 1)
+ if (status !== 1) {
+ return res.status(400).json({ error: "Seuls les enregistrements avec statut 'ENVOI TERMINÉ' peuvent être renvoyés." });
+ }
+
+ const base = safeBaseName(fileName);
+ if (!base) {
+ return res.status(400).json({ error: "Nom de fichier invalide." });
+ }
+
+ // Chemins des fichiers nécessaires
+ const subfolder = base.charAt(0).toUpperCase();
+ const nzbArchivePath = path.join(config.finishdirectory, subfolder, base + '.7z');
+ const jsonPath = path.join(config.infodirectory, base + '.json');
+ const bdinfoPath = path.join(config.infodirectory, base + '.bdinfo.txt');
+ const quicksummaryPath = path.join(config.infodirectory, base + '.quicksummary.txt');
+
+ // Vérifier que l'archive NZB existe
+ if (!fs.existsSync(nzbArchivePath)) {
+ return res.status(404).json({ error: "Archive NZB non trouvée." });
+ }
+
+ // Extraire le NZB temporairement
+ const tmpDir = path.join(__dirname, 'tmp');
+ fs.mkdirSync(tmpDir, { recursive: true });
+ const tmpNzbPath = path.join(tmpDir, base + '.nzb');
+
+ const extractCommand = `7z e "${nzbArchivePath}" -o"${tmpDir}" -y`;
+
+ exec(extractCommand, (extractError) => {
+ if (extractError) {
+ console.error(`Erreur lors de l'extraction: ${extractError.message}`);
+ return res.status(500).json({ error: "Erreur lors de l'extraction du NZB." });
+ }
+
+ // Vérifier si c'est un ISO (BDInfo) ou autre (JSON)
+ const isIso = fileName.toLowerCase().endsWith('.iso');
+ let curlCommand;
+
+ if (isIso) {
+ // Pour les ISO : bdinfo_full + bdinfo_mini + nzb
+ if (!fs.existsSync(bdinfoPath) || !fs.existsSync(quicksummaryPath)) {
+ fs.unlinkSync(tmpNzbPath);
+ return res.status(404).json({ error: "Fichiers BDInfo non trouvés." });
+ }
+
+ curlCommand = `curl -s -k -L -m 60 \\
+ -F rlsname=${base} \\
+ -F bdinfo_full=@${bdinfoPath} \\
+ -F bdinfo_mini=@${quicksummaryPath} \\
+ -F nzb=@${tmpNzbPath} \\
+ -F upload=upload "${config.apiUrl}${config.apiKey}"`;
+ } else {
+ // Pour les autres : generated_nfo_json + nzb
+ if (!fs.existsSync(jsonPath)) {
+ fs.unlinkSync(tmpNzbPath);
+ return res.status(404).json({ error: "Fichier JSON non trouvé." });
+ }
+
+ curlCommand = `curl -s -k -L -m 60 \\
+ -F rlsname=${base} \\
+ -F generated_nfo_json=@${jsonPath} \\
+ -F nzb=@${tmpNzbPath} \\
+ -F upload=upload "${config.apiUrl}${config.apiKey}"`;
+ }
+
+ // Exécuter la commande curl
+ exec(curlCommand, (curlError, stdout, stderr) => {
+ // Nettoyer le fichier temporaire
+ fs.unlinkSync(tmpNzbPath);
+
+ // Log des sorties curl pour debug
+ console.log(`=== CURL OUTPUT pour ${fileName} ===`);
+ console.log(`Command: ${curlCommand}`);
+ if (stdout) console.log(`STDOUT: ${stdout}`);
+ if (stderr) console.log(`STDERR: ${stderr}`);
+ console.log(`================================`);
+
+ if (curlError) {
+ console.error(`Erreur lors du renvoi: ${curlError.message}`);
+ return res.status(500).json({ error: "Erreur lors du renvoi vers le site principal." });
+ }
+
+ console.log(`Renvoi réussi pour ${fileName}`);
+ res.json({ success: true, message: "Renvoi effectué avec succès." });
+ });
+ });
+
+ } catch (err) {
+ console.error(err.message);
+ res.status(500).json({ error: "Erreur lors du renvoi." });
+ }
+});
+
// --------------------------- Filtrage -----------------------------
autopostRouter.get('/filter', async (req, res) => {
const status = Number.isFinite(parseInt(req.query.status, 10))
diff --git a/autopost/views/autopost.html b/autopost/views/autopost.html
index edb7650..94562cb 100644
--- a/autopost/views/autopost.html
+++ b/autopost/views/autopost.html
@@ -114,10 +114,28 @@
{{PAGINATION_HTML}}
+
+
+
+
0 élément sélectionné
+
+
+
+
+
+
+
+ |
+
+ |
Name |
Status |
ID |
@@ -187,6 +205,56 @@
+
+
+
+
+
Renvoyer cet enregistrement ?
+
Vous êtes sur le point de renvoyer vers le site principal.
+
+
+
+
+
+
+
+
+
+
+
+
✏️ Éditer la sélection
+
Modifier le statut de 0 élément(s) sélectionné(s).
+
+
+
+
+
+
+
+
+
Supprimer ces enregistrements ?
+
Vous êtes sur le point de supprimer 0 élément(s) sélectionné(s).
+
+
+
+
+
+
+
+
+
+
Supprimé.