Project-Image-Uploader/backend/src/routes/reorder.js
matthias.lotz cdb2aa95e6 feat: Add comprehensive test suite and admin API authentication
🧪 Testing Infrastructure (45 tests, 100% passing)
- Implemented Jest + Supertest framework for automated testing
- Unit tests: 5 tests for auth middleware (100% coverage)
- Integration tests: 40 tests covering admin, consent, migration, upload APIs
- Test execution time: ~10 seconds for full suite
- Coverage: 26% statements, 15% branches (realistic start)
- In-memory SQLite database for isolated testing
- Singleton server pattern for fast test execution
- Automatic cleanup and teardown

🔒 Admin API Authentication
- Bearer token authentication for all admin endpoints
- requireAdminAuth middleware with ADMIN_API_KEY validation
- Protected routes: /api/admin/*, /api/system/migration/migrate|rollback
- Complete authentication guide in AUTHENTICATION.md
- HTTP 403 for missing/invalid tokens, 500 if not configured
- Ready for production with token rotation support

📋 API Route Documentation
- Single Source of Truth: backend/src/routes/routeMappings.js
- Comprehensive route overview in backend/src/routes/README.md
- Express routing order documented (specific before generic)
- Frontend integration guide with authentication examples
- OpenAPI auto-generation integrated

🐛 Bug Fixes
- Fixed SQLite connection not properly awaited (caused test hangs)
- Fixed upload validation checking req.files.file before req.files
- Fixed Express route order (consent before admin router)
- Fixed test environment using /tmp for uploads (permission issues)

📚 Documentation Updates
- Updated README.md with testing and authentication features
- Updated README.dev.md with testing section and API development guide
- Updated CHANGELOG.md with complete feature documentation
- Updated FEATURE_PLAN-autogen-openapi.md (status: 100% complete)
- Added frontend/MIGRATION-GUIDE.md for frontend team

🚀 Frontend Impact
Frontend needs to add Bearer token to all /api/admin/* calls.
See frontend/MIGRATION-GUIDE.md for detailed instructions.

Test Status:  45/45 passing (100%)
Backend:  Production ready
Frontend: ⚠️ Migration required (see MIGRATION-GUIDE.md)
2025-11-16 18:08:48 +01:00

142 lines
4.6 KiB
JavaScript

const express = require('express');
const router = express.Router();
const GroupRepository = require('../repositories/GroupRepository');
/**
* @swagger
* /{groupId}/reorder:
* put:
* tags: [Admin]
* summary: Reorder images within a group
* description: Updates the display order of images in a group. All image IDs of the group must be provided in the desired order.
* parameters:
* - in: path
* name: groupId
* required: true
* schema:
* type: string
* example: "cTV24Yn-a"
* description: Unique identifier of the group
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - imageIds
* properties:
* imageIds:
* type: array
* items:
* type: integer
* example: [123, 456, 789]
* description: Array of image IDs in the new desired order
* responses:
* 200:
* description: Image order updated successfully
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "Image order updated successfully"
* data:
* type: object
* properties:
* groupId:
* type: string
* updatedImages:
* type: integer
* newOrder:
* type: array
* items:
* type: integer
* 400:
* description: Invalid request - missing or invalid imageIds
* 404:
* description: Group not found
* 500:
* description: Server error during reordering
*/
router.put('/:groupId/reorder', async (req, res) => {
try {
const { groupId } = req.params;
const { imageIds } = req.body;
// Input validation
if (!groupId) {
return res.status(400).json({
success: false,
message: 'Group ID is required'
});
}
if (!imageIds || !Array.isArray(imageIds) || imageIds.length === 0) {
return res.status(400).json({
success: false,
message: 'imageIds array is required and cannot be empty'
});
}
// Validate that all imageIds are numbers
const invalidIds = imageIds.filter(id => !Number.isInteger(id) || id <= 0);
if (invalidIds.length > 0) {
return res.status(400).json({
success: false,
message: `Invalid image IDs: ${invalidIds.join(', ')}. Image IDs must be positive integers`
});
}
// Log the reordering operation
console.log(`[REORDER] Group ${groupId}: Reordering ${imageIds.length} images`);
console.log(`[REORDER] New order: ${imageIds.join(' → ')}`);
// Execute the reordering
const result = await GroupRepository.updateImageOrder(groupId, imageIds);
// Success response
res.status(200).json({
success: true,
message: 'Image order updated successfully',
data: result
});
// Log success
console.log(`[REORDER] Success: Updated ${result.updatedImages} images in group ${groupId}`);
} catch (error) {
console.error(`[REORDER] Error reordering images in group ${req.params.groupId}:`, error.message);
// Handle specific error types
if (error.message.includes('not found')) {
return res.status(404).json({
success: false,
message: error.message
});
}
if (error.message.includes('Invalid image IDs') ||
error.message.includes('Missing image IDs') ||
error.message.includes('is required')) {
return res.status(400).json({
success: false,
message: error.message
});
}
// Generic server error
res.status(500).json({
success: false,
message: 'Internal server error during image reordering',
error: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
});
module.exports = router;