🧪 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)
142 lines
4.6 KiB
JavaScript
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; |