Spaces:
Paused
Paused
Download Controller/certificateManagement.js from imkrish/campverse-backend: direct link, hf CLI and curl.
- Browser
- Download file 26.8 kB
-
https://huggingface.co/spaces/imkrish/campverse-backend/resolve/main/Controller/certificateManagement.js
- Command line
-
hf download hf://spaces/imkrish/campverse-backend/Controller/certificateManagement.js
-
curl -L -o certificateManagement.js https://huggingface.co/spaces/imkrish/campverse-backend/resolve/main/Controller/certificateManagement.js
26.8 kB
| const Event = require('../Models/Event'); | |
| const EventParticipationLog = require('../Models/EventParticipationLog'); | |
| const Certificate = require('../Models/Certificate'); | |
| const User = require('../Models/User'); | |
| const axios = require('axios'); | |
| const { storageService } = require('../Services/storageService'); | |
| // Certificate Generator API URL (update based on your deployment) | |
| const CERTIFICATE_API_URL = process.env.CERTIFICATE_API_URL || 'http://localhost:8000'; | |
| /** | |
| * Update event certificate settings | |
| * Host can enable/disable certificates and configure settings | |
| */ | |
| async function updateCertificateSettings(req, res) { | |
| try { | |
| const { eventId } = req.params; | |
| const userId = req.user.id; | |
| const { | |
| certificateEnabled, | |
| certificateType, | |
| awardText, | |
| leftSignatory, | |
| rightSignatory, | |
| selectedTemplateId, // Template ID assigned by Admin/Verifier | |
| previewConfig, | |
| } = req.body; | |
| // Find event and verify host permission | |
| const event = await Event.findById(eventId); | |
| if (!event) { | |
| return res.status(404).json({ error: 'Event not found.' }); | |
| } | |
| const isHost = event.hostUserId.toString() === userId; | |
| if (!isHost) { | |
| return res.status(403).json({ | |
| error: 'Only the event host can update certificate settings.', | |
| }); | |
| } | |
| // Update certificate settings | |
| if (certificateEnabled !== undefined) { | |
| event.features = event.features || {}; | |
| event.features.certificateEnabled = certificateEnabled; | |
| } | |
| if (!event.certificateSettings) { | |
| event.certificateSettings = {}; | |
| } | |
| if (certificateType) { | |
| event.certificateSettings.certificateType = certificateType; | |
| } | |
| if (awardText !== undefined) { | |
| event.certificateSettings.awardText = awardText; | |
| } | |
| if (leftSignatory) { | |
| event.certificateSettings.leftSignatory = leftSignatory; | |
| } | |
| if (rightSignatory) { | |
| event.certificateSettings.rightSignatory = rightSignatory; | |
| } | |
| if (selectedTemplateId) { | |
| event.certificateSettings.selectedTemplateId = selectedTemplateId; | |
| } | |
| if (previewConfig !== undefined) { | |
| event.certificateSettings.previewConfig = previewConfig; | |
| } | |
| await event.save(); | |
| res.json({ | |
| message: 'Certificate settings updated successfully.', | |
| certificateEnabled: event.features.certificateEnabled, | |
| certificateSettings: event.certificateSettings, | |
| }); | |
| } catch (err) { | |
| console.error('Error updating certificate settings:', err); | |
| res.status(500).json({ error: 'Server error updating certificate settings.' }); | |
| } | |
| } | |
| /** | |
| * Upload certificate assets (template, logos, signatures) | |
| * Files are uploaded to cloud storage | |
| */ | |
| async function uploadCertificateAssets(req, res) { | |
| try { | |
| const { eventId } = req.params; | |
| const userId = req.user.id; | |
| const { assetType } = req.body; // 'template', 'orgLogo', 'leftSignature', 'rightSignature' | |
| // Verify event and host permission | |
| const event = await Event.findById(eventId); | |
| if (!event) { | |
| return res.status(404).json({ error: 'Event not found.' }); | |
| } | |
| const isHost = event.hostUserId.toString() === userId; | |
| if (!isHost) { | |
| return res.status(403).json({ | |
| error: 'Only the event host can upload certificate assets.', | |
| }); | |
| } | |
| // Check if certificates are enabled | |
| if (!event.features?.certificateEnabled) { | |
| return res.status(400).json({ | |
| error: 'Certificates are not enabled for this event.', | |
| }); | |
| } | |
| // Check if files were uploaded | |
| if (!req.files || (Array.isArray(req.files) && req.files.length === 0)) { | |
| return res.status(400).json({ error: 'No files uploaded.' }); | |
| } | |
| // DISALLOW hosts from uploading templates (requested: Centralized Template Management) | |
| if (assetType === 'template' && req.user.role !== 'admin' && req.user.role !== 'platformAdmin') { | |
| return res.status(403).json({ | |
| error: 'Only administrators can upload custom certificate templates. Please select from the predefined templates.' | |
| }); | |
| } | |
| const uploadedFiles = []; | |
| // Initialize certificate assets structure | |
| if (!event.certificateSettings) { | |
| event.certificateSettings = {}; | |
| } | |
| if (!event.certificateSettings.uploadedAssets) { | |
| event.certificateSettings.uploadedAssets = {}; | |
| } | |
| // Upload files to cloud storage | |
| for (const file of req.files) { | |
| try { | |
| // Normalize assetType from frontend | |
| let effectiveAssetType = assetType; | |
| if (assetType === 'logo') { | |
| effectiveAssetType = req.body.logo_type === 'right' ? 'rightLogo' : 'orgLogo'; | |
| } else if (assetType === 'signature') { | |
| effectiveAssetType = req.body.signature_type === 'right' ? 'rightSignature' : 'leftSignature'; | |
| } | |
| switch (effectiveAssetType) { | |
| case 'template': | |
| const templateType = req.body.template_type || 'participation'; | |
| uploadResult = await storageService.uploadCertificateTemplate( | |
| file.buffer, | |
| file.originalname, | |
| eventId, | |
| templateType, | |
| file.mimetype | |
| ); | |
| // Store template info | |
| event.certificateSettings.uploadedAssets.templateUrl = uploadResult.url; | |
| event.certificateSettings.uploadedAssets.templatePath = uploadResult.path; | |
| event.certificateSettings.certificateType = templateType; | |
| break; | |
| case 'orgLogo': | |
| case 'leftLogo': | |
| uploadResult = await storageService.uploadCertificateLogo( | |
| file.buffer, | |
| file.originalname, | |
| eventId, | |
| 'left', | |
| file.mimetype | |
| ); | |
| // Store org logo info | |
| event.certificateSettings.uploadedAssets.organizationLogo = uploadResult.url; | |
| event.certificateSettings.uploadedAssets.orgLogoPath = uploadResult.path; | |
| break; | |
| case 'rightLogo': | |
| uploadResult = await storageService.uploadCertificateLogo( | |
| file.buffer, | |
| file.originalname, | |
| eventId, | |
| 'right', | |
| file.mimetype | |
| ); | |
| // Store right logo info | |
| event.certificateSettings.uploadedAssets.rightLogoUrl = uploadResult.url; | |
| event.certificateSettings.uploadedAssets.rightLogoPath = uploadResult.path; | |
| break; | |
| case 'leftSignature': | |
| uploadResult = await storageService.uploadCertificateSignature( | |
| file.buffer, | |
| file.originalname, | |
| eventId, | |
| 'left', | |
| file.mimetype | |
| ); | |
| // Store left signature info | |
| if (!event.certificateSettings.leftSignatory) { | |
| event.certificateSettings.leftSignatory = {}; | |
| } | |
| event.certificateSettings.leftSignatory.signatureUrl = uploadResult.url; | |
| event.certificateSettings.leftSignatory.signaturePath = uploadResult.path; | |
| break; | |
| case 'rightSignature': | |
| uploadResult = await storageService.uploadCertificateSignature( | |
| file.buffer, | |
| file.originalname, | |
| eventId, | |
| 'right', | |
| file.mimetype | |
| ); | |
| // Store right signature info | |
| if (!event.certificateSettings.rightSignatory) { | |
| event.certificateSettings.rightSignatory = {}; | |
| } | |
| event.certificateSettings.rightSignatory.signatureUrl = uploadResult.url; | |
| event.certificateSettings.rightSignatory.signaturePath = uploadResult.path; | |
| break; | |
| default: | |
| console.warn(`[Cert] Unknown effective asset type: ${effectiveAssetType}`); | |
| continue; | |
| } | |
| uploadedFiles.push({ | |
| originalName: file.originalname, | |
| assetType, | |
| url: uploadResult.url, | |
| path: uploadResult.path, | |
| }); | |
| } catch (uploadError) { | |
| console.error('Error uploading asset:', uploadError.message); | |
| throw new Error(`Failed to upload ${file.originalname}: ${uploadError.message}`); | |
| } | |
| } | |
| // Save event with updated assets | |
| await event.save(); | |
| res.json({ | |
| message: 'Assets uploaded successfully to cloud storage.', | |
| uploadedFiles, | |
| certificateSettings: event.certificateSettings, | |
| }); | |
| } catch (err) { | |
| console.error('Error uploading certificate assets:', err); | |
| res.status(500).json({ error: err.message || 'Server error uploading assets.' }); | |
| } | |
| } | |
| /** | |
| * Generate certificates for all attended participants | |
| * This is triggered by the host after the event | |
| */ | |
| async function generateCertificates(req, res) { | |
| try { | |
| const { eventId } = req.params; | |
| const userId = req.user.id; | |
| // Verify event and host permission | |
| const event = await Event.findById(eventId); | |
| if (!event) { | |
| return res.status(404).json({ error: 'Event not found.' }); | |
| } | |
| const isHost = event.hostUserId.toString() === userId; | |
| if (!isHost) { | |
| return res.status(403).json({ | |
| error: 'Only the event host can generate certificates.', | |
| }); | |
| } | |
| // Check if certificates are enabled | |
| if (!event.features?.certificateEnabled) { | |
| return res.status(400).json({ | |
| error: 'Certificates are not enabled for this event.', | |
| }); | |
| } | |
| // Check if certificate setup is approved | |
| if (event.certificateSettings?.verificationStatus !== 'approved') { | |
| return res.status(400).json({ | |
| error: 'Certificate setup must be approved by a verifier before generation.', | |
| currentStatus: event.certificateSettings?.verificationStatus || 'not_configured', | |
| }); | |
| } | |
| // Check if required assets are uploaded - using uploadedAssets structure | |
| const uploadedAssets = event.certificateSettings?.uploadedAssets || {}; | |
| const settings = event.certificateSettings || {}; | |
| // Template can come from upload OR predefined selection | |
| const PREDEFINED_TEMPLATES = { | |
| 'classic-blue': 'https://storage.campverse.com/templates/classic-blue.png', | |
| 'modern-purple': 'https://storage.campverse.com/templates/modern-purple.png', | |
| 'elegant-gold': 'https://storage.campverse.com/templates/elegant-gold.png', | |
| 'minimal-dark': 'https://storage.campverse.com/templates/minimal-dark.png', | |
| }; | |
| const templateUrl = uploadedAssets.templateUrl || PREDEFINED_TEMPLATES[settings.selectedTemplateId]; | |
| if (!templateUrl || !uploadedAssets.organizationLogo || | |
| !uploadedAssets.leftSignature || !uploadedAssets.rightSignature) { | |
| return res.status(400).json({ | |
| error: 'All required assets must be uploaded (template/selection, organization logo, and both signatures).', | |
| uploaded: { | |
| template: !!templateUrl, | |
| orgLogo: !!uploadedAssets.organizationLogo, | |
| leftSignature: !!uploadedAssets.leftSignature, | |
| rightSignature: !!uploadedAssets.rightSignature, | |
| }, | |
| }); | |
| } | |
| // Get all attended participants | |
| const attendedLogs = await EventParticipationLog.find({ | |
| eventId, | |
| status: 'attended', | |
| }).populate('userId', 'name email'); | |
| if (attendedLogs.length === 0) { | |
| return res.status(400).json({ | |
| error: 'No attended participants found for this event.', | |
| }); | |
| } | |
| // Prepare batch generation request - use the corrected variables | |
| const certificateSettings = event.certificateSettings; | |
| const batchRequest = { | |
| eventId: eventId, | |
| eventTitle: event.title, | |
| templateUrl: templateUrl, // Already resolved above with fallback | |
| orgLogoUrl: uploadedAssets.organizationLogo, | |
| leftSignature: { | |
| url: uploadedAssets.leftSignature, | |
| name: certificateSettings.leftSignatory?.name || '', | |
| title: certificateSettings.leftSignatory?.title || '', | |
| }, | |
| rightSignature: { | |
| url: uploadedAssets.rightSignature, | |
| name: certificateSettings.rightSignatory?.name || '', | |
| title: certificateSettings.rightSignatory?.title || '', | |
| }, | |
| awardText: certificateSettings.awardText || 'For successfully participating in', | |
| certificateType: certificateSettings.certificateType || 'participation', | |
| participants: attendedLogs.map((log) => ({ | |
| userId: log.userId._id.toString(), | |
| name: log.userId.name, | |
| email: log.userId.email, | |
| })), | |
| }; | |
| // Call ML batch generation endpoint - NOT storing PDFs, just validating generation | |
| // Certificates will be rendered on-demand when requested | |
| let generateResponse; | |
| try { | |
| // Send a test generation request to validate assets | |
| generateResponse = await axios.post( | |
| `${CERTIFICATE_API_URL}/batch-validate`, | |
| batchRequest, | |
| { timeout: 30000 } // 30 second timeout just for validation | |
| ); | |
| } catch (apiError) { | |
| console.error('Error validating certificate generation:', apiError.message); | |
| return res.status(500).json({ | |
| error: 'Failed to validate certificate generation from ML service.', | |
| details: apiError.message, | |
| }); | |
| } | |
| // Create certificate records with metadata only (NO URLs stored) | |
| // Certificates will be generated on-demand when users request them | |
| const certificateRecords = []; | |
| for (const log of attendedLogs) { | |
| // Check if certificate record already exists | |
| let certificate = await Certificate.findOne({ | |
| userId: log.userId._id, | |
| eventId: eventId, | |
| }); | |
| if (!certificate) { | |
| certificate = await Certificate.create({ | |
| userId: log.userId._id, | |
| eventId: eventId, | |
| type: certificateSettings.certificateType === 'achievement' ? 'winner' : 'participant', // REQUIRED field | |
| status: 'ready', // Status indicates certificate is ready to be rendered | |
| issuedAt: new Date(), | |
| // NO certificateUrl - certificates are rendered on-demand | |
| }); | |
| } else { | |
| // Update existing certificate | |
| certificate.status = 'ready'; | |
| certificate.updatedAt = new Date(); | |
| await certificate.save(); | |
| } | |
| certificateRecords.push({ | |
| userId: log.userId._id, | |
| userName: log.userId.name, | |
| email: log.userId.email, | |
| status: 'ready', | |
| renderUrl: `/api/certificate-management/events/${eventId}/render/${log.userId._id}`, // Correct path | |
| }); | |
| } | |
| // Update event with generation timestamp | |
| event.certificateSettings.lastGeneratedAt = new Date(); | |
| event.certificateSettings.certificatesReady = true; | |
| await event.save(); | |
| res.json({ | |
| message: `Successfully prepared ${certificateRecords.length} certificate(s) for on-demand rendering.`, | |
| note: 'Certificates are not stored. They will be generated when users download them.', | |
| certificates: certificateRecords, | |
| totalGenerated: certificateRecords.length, | |
| totalParticipants: attendedLogs.length, | |
| }); | |
| } catch (err) { | |
| console.error('Error generating certificates:', err); | |
| res.status(500).json({ | |
| error: 'Server error generating certificates.', | |
| details: err.message, | |
| }); | |
| } | |
| } | |
| /** | |
| * Get certificate status for an event | |
| * Returns list of participants and their certificate status | |
| */ | |
| async function getCertificateStatus(req, res) { | |
| try { | |
| const { eventId } = req.params; | |
| const userId = req.user.id; | |
| // Verify event and host permission | |
| const event = await Event.findById(eventId); | |
| if (!event) { | |
| return res.status(404).json({ error: 'Event not found.' }); | |
| } | |
| const isHost = event.hostUserId.toString() === userId; | |
| const isCoHost = event.coHosts?.some((id) => id.toString() === userId); | |
| if (!isHost && !isCoHost) { | |
| return res.status(403).json({ | |
| error: 'Only host or co-host can view certificate status.', | |
| }); | |
| } | |
| // Get all attended participants | |
| const attendedLogs = await EventParticipationLog.find({ | |
| eventId, | |
| status: 'attended', | |
| }).populate('userId', 'name email'); | |
| // Get certificate records | |
| const certificates = await Certificate.find({ eventId }); | |
| const certificateMap = {}; | |
| certificates.forEach((cert) => { | |
| certificateMap[cert.userId.toString()] = cert; | |
| }); | |
| // Build status list | |
| const statusList = attendedLogs.map((log) => { | |
| const cert = certificateMap[log.userId._id.toString()]; | |
| return { | |
| userId: log.userId._id, | |
| name: log.userId.name, | |
| email: log.userId.email, | |
| attendedAt: log.attendanceTimestamp, | |
| certificateGenerated: !!cert, | |
| certificateUrl: cert?.certificateUrl, | |
| certificateStatus: cert?.status, | |
| generatedAt: cert?.generatedAt, | |
| }; | |
| }); | |
| res.json({ | |
| eventTitle: event.title, | |
| certificateEnabled: event.features?.certificateEnabled || false, | |
| totalAttended: attendedLogs.length, | |
| certificatesGenerated: certificates.length, | |
| participants: statusList, | |
| }); | |
| } catch (err) { | |
| console.error('Error fetching certificate status:', err); | |
| res.status(500).json({ error: 'Server error fetching certificate status.' }); | |
| } | |
| } | |
| /** | |
| * Get user's certificate for a specific event | |
| */ | |
| async function getUserCertificate(req, res) { | |
| try { | |
| const { eventId } = req.params; | |
| const userId = req.user.id; | |
| // Check if user attended the event | |
| const log = await EventParticipationLog.findOne({ | |
| eventId, | |
| userId, | |
| status: 'attended', | |
| }); | |
| if (!log) { | |
| return res.status(404).json({ | |
| error: 'Certificate not available.', | |
| message: 'You must attend the event to receive a certificate.', | |
| }); | |
| } | |
| // Find certificate | |
| const certificate = await Certificate.findOne({ | |
| eventId, | |
| userId, | |
| }).populate('eventId', 'title date'); | |
| if (!certificate) { | |
| return res.status(404).json({ | |
| error: 'Certificate not generated yet.', | |
| message: 'The event host has not generated certificates yet.', | |
| }); | |
| } | |
| res.json({ | |
| certificate: { | |
| certificateId: certificate._id, | |
| eventTitle: certificate.eventId.title, | |
| eventDate: certificate.eventId.date, | |
| certificateUrl: certificate.certificateUrl, | |
| generatedAt: certificate.generatedAt, | |
| status: certificate.status, | |
| }, | |
| }); | |
| } catch (err) { | |
| console.error('Error fetching user certificate:', err); | |
| res.status(500).json({ error: 'Server error fetching certificate.' }); | |
| } | |
| } | |
| /** | |
| * Regenerate certificates (if needed) | |
| */ | |
| async function regenerateCertificates(req, res) { | |
| try { | |
| const { eventId } = req.params; | |
| const userId = req.user.id; | |
| // Verify event and host permission | |
| const event = await Event.findById(eventId); | |
| if (!event) { | |
| return res.status(404).json({ error: 'Event not found.' }); | |
| } | |
| const isHost = event.hostUserId.toString() === userId; | |
| if (!isHost) { | |
| return res.status(403).json({ | |
| error: 'Only the event host can regenerate certificates.', | |
| }); | |
| } | |
| // Delete existing certificates | |
| await Certificate.deleteMany({ eventId }); | |
| // Regenerate | |
| return generateCertificates(req, res); | |
| } catch (err) { | |
| console.error('Error regenerating certificates:', err); | |
| res.status(500).json({ error: 'Server error regenerating certificates.' }); | |
| } | |
| } | |
| /** | |
| * Bulk upload participant data via CSV for certificate generation | |
| */ | |
| async function bulkUploadParticipants(req, res) { | |
| try { | |
| const { eventId } = req.params; | |
| const userId = req.user.id; | |
| // Verify event and host permission | |
| const event = await Event.findById(eventId); | |
| if (!event) { | |
| return res.status(404).json({ error: 'Event not found.' }); | |
| } | |
| const isHost = event.hostUserId.toString() === userId; | |
| if (!isHost) { | |
| return res.status(403).json({ | |
| error: 'Only the event host can upload participant data.', | |
| }); | |
| } | |
| // This endpoint expects a CSV file with participant data | |
| // The CSV should be parsed and used to update/verify participant records | |
| // Implementation depends on your CSV parsing library | |
| res.json({ | |
| message: 'Bulk upload functionality - to be implemented with CSV parser.', | |
| }); | |
| } catch (err) { | |
| console.error('Error in bulk upload:', err); | |
| res.status(500).json({ error: 'Server error in bulk upload.' }); | |
| } | |
| } | |
| /** | |
| * Render certificate on-demand for a specific user | |
| * Generates the certificate in real-time and streams it to the response | |
| */ | |
| async function renderCertificate(req, res) { | |
| try { | |
| const { eventId, userId } = req.params; | |
| const requestingUserId = req.user.id; | |
| // Check if user is requesting their own certificate or is the host | |
| const event = await Event.findById(eventId); | |
| if (!event) { | |
| return res.status(404).json({ error: 'Event not found.' }); | |
| } | |
| const isHost = event.hostUserId.toString() === requestingUserId; | |
| const isOwnCertificate = userId === requestingUserId; | |
| const isVerifier = req.user.roles.includes('verifier'); | |
| const isAdmin = req.user.roles.includes('platformAdmin'); | |
| if (!isHost && !isOwnCertificate && !isVerifier && !isAdmin) { | |
| return res.status(403).json({ | |
| error: 'You can only access your own certificate or certificates from events you host.', | |
| }); | |
| } | |
| // Check if certificate is ready | |
| const certificate = await Certificate.findOne({ | |
| eventId, | |
| userId, | |
| status: 'ready', | |
| }); | |
| if (!certificate) { | |
| return res.status(404).json({ | |
| error: 'Certificate not available.', | |
| message: 'Certificate has not been generated yet or you did not attend this event.', | |
| }); | |
| } | |
| // Get user details | |
| const user = await User.findById(userId); | |
| if (!user) { | |
| return res.status(404).json({ error: 'User not found.' }); | |
| } | |
| // Get certificate settings | |
| const certificateSettings = event.certificateSettings; | |
| const uploadedAssets = certificateSettings?.uploadedAssets || {}; | |
| // Template can come from upload OR predefined selection | |
| const PREDEFINED_TEMPLATES = { | |
| 'classic-blue': 'https://storage.campverse.com/templates/classic-blue.png', | |
| 'modern-purple': 'https://storage.campverse.com/templates/modern-purple.png', | |
| 'elegant-gold': 'https://storage.campverse.com/templates/elegant-gold.png', | |
| 'minimal-dark': 'https://storage.campverse.com/templates/minimal-dark.png', | |
| }; | |
| const templateUrl = uploadedAssets.templateUrl || PREDEFINED_TEMPLATES[certificateSettings?.selectedTemplateId]; | |
| if (!templateUrl) { | |
| return res.status(500).json({ | |
| error: 'Certificate template not configured.', | |
| }); | |
| } | |
| // Prepare single certificate generation request | |
| const renderRequest = { | |
| eventId: eventId, | |
| eventTitle: event.title, | |
| templateUrl: templateUrl, | |
| orgLogoUrl: uploadedAssets.organizationLogo, | |
| rightLogoUrl: uploadedAssets.rightLogoUrl || 'https://campverse.com/logo.png', // Default CampVerse logo | |
| leftSignature: { | |
| url: uploadedAssets.leftSignature, | |
| name: certificateSettings.leftSignatory?.name || '', | |
| title: certificateSettings.leftSignatory?.title || '', | |
| }, | |
| rightSignature: { | |
| url: uploadedAssets.rightSignature, | |
| name: certificateSettings.rightSignatory?.name || '', | |
| title: certificateSettings.rightSignatory?.title || '', | |
| }, | |
| awardText: certificateSettings.awardText || 'For successfully participating in', | |
| certificateType: certificateSettings.certificateType || 'participation', | |
| participant: { | |
| userId: userId, | |
| name: user.name, | |
| email: user.email, | |
| }, | |
| }; | |
| // Call ML single certificate render endpoint | |
| try { | |
| const renderResponse = await axios.post( | |
| `${CERTIFICATE_API_URL}/render-certificate`, | |
| renderRequest, | |
| { | |
| responseType: 'arraybuffer', // Get PDF as binary data | |
| timeout: 30000, // 30 second timeout | |
| } | |
| ); | |
| // Stream the PDF directly to the response | |
| res.setHeader('Content-Type', 'application/pdf'); | |
| res.setHeader( | |
| 'Content-Disposition', | |
| `attachment; filename="${user.name.replace(/[^a-zA-Z0-9]/g, '_')}_Certificate.pdf"` | |
| ); | |
| res.send(renderResponse.data); | |
| } catch (apiError) { | |
| console.error('Error rendering certificate:', apiError.message); | |
| return res.status(500).json({ | |
| error: 'Failed to render certificate.', | |
| details: apiError.message, | |
| }); | |
| } | |
| } catch (err) { | |
| console.error('Error in renderCertificate:', err); | |
| res.status(500).json({ | |
| error: 'Server error rendering certificate.', | |
| details: err.message, | |
| }); | |
| } | |
| } | |
| /** | |
| * Submit certificate configuration for verification | |
| */ | |
| async function submitCertificateConfig(req, res) { | |
| try { | |
| const { eventId } = req.params; | |
| const userId = req.user.id; | |
| // Find event and verify host permission | |
| const event = await Event.findById(eventId); | |
| if (!event) { | |
| return res.status(404).json({ error: 'Event not found.' }); | |
| } | |
| const isHost = event.hostUserId.toString() === userId; | |
| if (!isHost) { | |
| return res.status(403).json({ | |
| error: 'Only the event host can submit certificate configuration.', | |
| }); | |
| } | |
| // Check if certificates are enabled | |
| if (!event.features?.certificateEnabled) { | |
| return res.status(400).json({ | |
| error: 'Certificates are not enabled for this event.', | |
| }); | |
| } | |
| // Validate if basic settings exist | |
| const settings = event.certificateSettings; | |
| if (!settings?.awardText || !settings?.leftSignatory?.name || !settings?.rightSignatory?.name) { | |
| return res.status(400).json({ | |
| error: 'Please configure Award Text and both Signatories before submitting.', | |
| }); | |
| } | |
| // Update status | |
| event.certificateSettings.verificationStatus = 'pending'; | |
| event.certificateSettings.submittedBy = userId; | |
| event.certificateSettings.submittedAt = new Date(); | |
| await event.save(); | |
| res.json({ | |
| message: 'Certificate configuration submitted for verification.', | |
| verificationStatus: 'pending', | |
| }); | |
| } catch (err) { | |
| console.error('Error submitting certificate configuration:', err); | |
| res.status(500).json({ error: 'Server error submitting configuration.' }); | |
| } | |
| } | |
| module.exports = { | |
| updateCertificateSettings, | |
| uploadCertificateAssets, | |
| generateCertificates, | |
| getCertificateStatus, | |
| getUserCertificate, | |
| regenerateCertificates, | |
| bulkUploadParticipants, | |
| renderCertificate, | |
| submitCertificateConfig, | |
| }; | |