Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d05ff6380e | |||
| 605f773a7e |
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "picpeak-backend",
|
||||
"version": "1.0.37",
|
||||
"version": "1.0.38",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "picpeak-backend",
|
||||
"version": "1.0.37",
|
||||
"version": "1.0.38",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.16",
|
||||
"archiver": "^5.3.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "picpeak-backend",
|
||||
"version": "1.0.37",
|
||||
"version": "1.0.38",
|
||||
"description": "Backend for PicPeak event photo sharing platform",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Script to test photo authentication
|
||||
* Usage: node scripts/test-photo-auth.js <jwt-token>
|
||||
*/
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
async function testPhotoAuth(token) {
|
||||
if (!token) {
|
||||
console.error('Usage: node scripts/test-photo-auth.js <jwt-token>');
|
||||
console.error('\nTo get a token, login to a gallery and check localStorage for gallery_token_<slug>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const baseUrl = process.env.API_URL || 'http://localhost:3001';
|
||||
|
||||
console.log(`Testing photo authentication with token: ${token.substring(0, 20)}...`);
|
||||
console.log(`Base URL: ${baseUrl}\n`);
|
||||
|
||||
// Test URLs
|
||||
const tests = [
|
||||
{
|
||||
name: 'Thumbnail via static route',
|
||||
url: `${baseUrl}/thumbnails/thumb_Test_Gallery_uncategorized_5210.jpg`,
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
},
|
||||
{
|
||||
name: 'Photo via static route',
|
||||
url: `${baseUrl}/photos/wedding-test-gallery-2025-07-14-1/Test_Gallery_uncategorized_5210.jpg`,
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
},
|
||||
{
|
||||
name: 'Gallery photos API',
|
||||
url: `${baseUrl}/api/gallery/wedding-test-gallery-2025-07-14-1/photos`,
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
console.log(`Testing: ${test.name}`);
|
||||
console.log(`URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const response = await axios.get(test.url, {
|
||||
headers: test.headers,
|
||||
validateStatus: () => true // Don't throw on any status
|
||||
});
|
||||
|
||||
console.log(`Status: ${response.status}`);
|
||||
console.log(`Headers:`, response.headers['content-type']);
|
||||
|
||||
if (response.status === 200) {
|
||||
if (test.name.includes('API')) {
|
||||
console.log(`Photos count: ${response.data.photos?.length || 0}`);
|
||||
} else {
|
||||
console.log(`Content length: ${response.headers['content-length']} bytes`);
|
||||
}
|
||||
} else {
|
||||
console.log(`Error:`, response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Network error:`, error.message);
|
||||
}
|
||||
|
||||
console.log('---\n');
|
||||
}
|
||||
|
||||
// Decode token to show info
|
||||
try {
|
||||
const parts = token.split('.');
|
||||
const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
|
||||
console.log('Token payload:', payload);
|
||||
} catch (error) {
|
||||
console.log('Failed to decode token');
|
||||
}
|
||||
}
|
||||
|
||||
// Get token from command line
|
||||
const token = process.argv[2];
|
||||
|
||||
testPhotoAuth(token).catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -136,8 +136,8 @@ router.get('/:slug/photos', verifyGalleryAccess, async (req, res) => {
|
||||
photos: photos.map(photo => ({
|
||||
id: photo.id,
|
||||
filename: photo.filename,
|
||||
url: `/photos/${photo.path}`,
|
||||
thumbnail_url: photo.thumbnail_path ? `/${photo.thumbnail_path}` : null,
|
||||
url: `/api/gallery/${req.params.slug}/photo/${photo.id}`,
|
||||
thumbnail_url: photo.thumbnail_path ? `/api/gallery/${req.params.slug}/thumbnail/${photo.id}` : null,
|
||||
type: photo.type,
|
||||
category_id: photo.category_id,
|
||||
category_name: photo.category_name,
|
||||
@@ -320,6 +320,42 @@ router.get('/:slug/photo/:photoId', verifyGalleryAccess, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Serve thumbnail
|
||||
router.get('/:slug/thumbnail/:photoId', verifyGalleryAccess, async (req, res) => {
|
||||
try {
|
||||
const { photoId } = req.params;
|
||||
|
||||
const photo = await db('photos')
|
||||
.where({ id: photoId, event_id: req.event.id })
|
||||
.first();
|
||||
|
||||
if (!photo || !photo.thumbnail_path) {
|
||||
return res.status(404).json({ error: 'Thumbnail not found' });
|
||||
}
|
||||
|
||||
const thumbPath = path.join(getStoragePath(), photo.thumbnail_path);
|
||||
|
||||
// Check if file exists
|
||||
const fs = require('fs').promises;
|
||||
try {
|
||||
await fs.access(thumbPath);
|
||||
} catch (error) {
|
||||
return res.status(404).json({ error: 'Thumbnail file not found' });
|
||||
}
|
||||
|
||||
// Set appropriate headers
|
||||
res.setHeader('Content-Type', 'image/jpeg');
|
||||
res.setHeader('Cache-Control', 'private, max-age=3600');
|
||||
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
|
||||
|
||||
// Send file
|
||||
res.sendFile(path.resolve(thumbPath));
|
||||
} catch (error) {
|
||||
console.error('Error serving thumbnail:', error);
|
||||
res.status(500).json({ error: 'Failed to serve thumbnail' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get photo stats
|
||||
router.get('/:slug/stats', verifyGalleryAccess, async (req, res) => {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Nginx Configuration Fix for Photo Authentication
|
||||
|
||||
If photos and thumbnails are not loading in gallery view but work in admin, it's likely that the Authorization header is being stripped by nginx or another reverse proxy.
|
||||
|
||||
## Common Issue
|
||||
|
||||
The `Authorization` header is often not passed through by default in nginx proxy configurations.
|
||||
|
||||
## Fix
|
||||
|
||||
Add these lines to your nginx configuration for the PicPeak location block:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://localhost:3001;
|
||||
|
||||
# Important: Pass the Authorization header
|
||||
proxy_pass_header Authorization;
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
|
||||
# Other standard proxy headers
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
```
|
||||
|
||||
## Alternative Fix Using Traefik
|
||||
|
||||
If using Traefik, ensure headers are passed:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
picpeak:
|
||||
labels:
|
||||
- "traefik.http.middlewares.picpeak-headers.headers.customrequestheaders.Authorization="
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
1. Check if Authorization header is reaching the backend:
|
||||
```bash
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" https://picpeak.yourdomain.com/thumbnails/test.jpg -v
|
||||
```
|
||||
|
||||
2. Check nginx logs to see if the header is present:
|
||||
```bash
|
||||
tail -f /var/log/nginx/access.log
|
||||
```
|
||||
|
||||
## Docker Compose Fix
|
||||
|
||||
If using docker-compose with nginx proxy, add:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- NGINX_PROXY_PASS_HEADER=Authorization
|
||||
```
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "picpeak-frontend",
|
||||
"version": "1.0.37",
|
||||
"version": "1.0.38",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "picpeak-frontend",
|
||||
"version": "1.0.37",
|
||||
"version": "1.0.38",
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.0.0",
|
||||
"@tiptap/extension-link": "^2.25.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "picpeak-frontend",
|
||||
"private": true,
|
||||
"version": "1.0.37",
|
||||
"version": "1.0.38",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
Reference in New Issue
Block a user