Fix admin gallery auth: use X-Admin-Password header instead of Authorization Bearer
NPMplus proxy strips the Authorization header before it reaches the backend, causing all write operations to return 401. Switch to a custom X-Admin-Password header which passes through the proxy unmodified. - backend/routes/photos.js: requireAuth checks X-Admin-Password first, falls back to Authorization Bearer for compatibility - backend/server.js: add X-Admin-Password to CORS allowedHeaders - admin/admin.js: send X-Admin-Password instead of Authorization Bearer in all fetch calls and XHR upload requests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
95e88780ab
commit
1a1fe7044a
@ -406,7 +406,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${getAdminPassword()}`,
|
||||
'X-Admin-Password': getAdminPassword(),
|
||||
},
|
||||
body: JSON.stringify(updatedPhoto)
|
||||
});
|
||||
@ -437,7 +437,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
try {
|
||||
await fetch(`${backendUrl}/photos/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${getAdminPassword()}` }
|
||||
headers: { 'X-Admin-Password': getAdminPassword() }
|
||||
});
|
||||
fetchPhotos();
|
||||
} catch (error) {
|
||||
@ -468,7 +468,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
try {
|
||||
await Promise.all(ids.map(id => fetch(`${backendUrl}/photos/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${getAdminPassword()}` }
|
||||
headers: { 'X-Admin-Password': getAdminPassword() }
|
||||
})));
|
||||
clearSelection();
|
||||
fetchPhotos();
|
||||
@ -528,7 +528,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${getAdminPassword()}`,
|
||||
'X-Admin-Password': getAdminPassword(),
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
@ -740,7 +740,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
xhr.open('POST', `${backendUrl}/photos/upload`);
|
||||
xhr.setRequestHeader('Authorization', `Bearer ${getAdminPassword()}`);
|
||||
xhr.setRequestHeader('X-Admin-Password', getAdminPassword());
|
||||
|
||||
uploadButton.classList.add('is-loading');
|
||||
uploadProgress.style.display = 'block';
|
||||
@ -832,7 +832,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const url = idx !== '' ? `${backendUrl}/photos/presets/${idx}` : `${backendUrl}/photos/presets`;
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${getAdminPassword()}` },
|
||||
headers: { 'Content-Type': 'application/json', 'X-Admin-Password': getAdminPassword() },
|
||||
body: JSON.stringify({ name, tags })
|
||||
});
|
||||
if (res.ok) {
|
||||
@ -853,7 +853,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (!confirm('Delete this preset?')) return;
|
||||
const res = await fetch(`${backendUrl}/photos/presets/${idx}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${getAdminPassword()}` }
|
||||
headers: { 'X-Admin-Password': getAdminPassword() }
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
@ -1024,7 +1024,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${getAdminPassword()}`,
|
||||
'X-Admin-Password': getAdminPassword(),
|
||||
},
|
||||
body: JSON.stringify({ data })
|
||||
});
|
||||
|
||||
@ -24,8 +24,11 @@ const WATERMARK_URL = process.env.WATERMARK_URL || 'http://watermarker:8000/wate
|
||||
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || '';
|
||||
|
||||
function requireAuth(req, res, next) {
|
||||
const auth = req.headers['authorization'] || '';
|
||||
const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
|
||||
// Accept password via custom header (proxy-safe) or legacy Authorization bearer
|
||||
const custom = req.headers['x-admin-password'] || '';
|
||||
const auth = req.headers['authorization'] || '';
|
||||
const bearer = auth.startsWith('Bearer ') ? auth.slice(7) : '';
|
||||
const token = custom || bearer;
|
||||
if (!token || !ADMIN_PASSWORD || token !== ADMIN_PASSWORD) {
|
||||
return res.status(401).json({ success: false, error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ const mongoose = require('mongoose');
|
||||
const app = express();
|
||||
const port = process.env.PORT || 5000;
|
||||
|
||||
app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'] }));
|
||||
app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Admin-Password'] }));
|
||||
app.use(express.json());
|
||||
app.use('/uploads', express.static('uploads'));
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user