diff --git a/checklist.txt b/checklist.txt index a4a268e..ab3c1a7 100644 --- a/checklist.txt +++ b/checklist.txt @@ -13,3 +13,15 @@ ADD COLUMN is_flagged BOOLEAN DEFAULT FALSE; ALTER TABLE interaction_logs ADD COLUMN report_text TEXT AFTER is_flagged; +CREATE TABLE IF NOT EXISTS app_versions ( + id INT AUTO_INCREMENT PRIMARY KEY, + platform ENUM('android', 'ios') NOT NULL, + latest_version VARCHAR(20) NOT NULL, + stable_version VARCHAR(20) NOT NULL, + under_maintenance BOOLEAN DEFAULT FALSE, + last_updated DATE NOT NULL, + app_url VARCHAR(500) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY unique_platform (platform) +); \ No newline at end of file diff --git a/src/app.js b/src/app.js index 59f3a52..e2f62d5 100644 --- a/src/app.js +++ b/src/app.js @@ -33,6 +33,7 @@ const appUserRoutes = require('./routes/appUsers'); const excelDataRoutes = require('./routes/exceldata'); const feedbackRoute = require('./routes/feedbacks'); const analyticsRoute = require('./routes/analysis'); +const appVersionRoutes = require('./routes/appVersioning'); // Import services const { refreshExpiredTokens } = require('./services/cronJobs'); @@ -102,6 +103,7 @@ async function startServer() { app.use('/api/process_excel', excelDataRoutes); app.use('/api/feedbacks', feedbackRoute); app.use('/api/analytics', analyticsRoute); + app.use('/api/app-versioning', appVersionRoutes); // Health check endpoint app.get('/health', (req, res) => { diff --git a/src/controllers/appVersionController.js b/src/controllers/appVersionController.js new file mode 100644 index 0000000..4d39bcc --- /dev/null +++ b/src/controllers/appVersionController.js @@ -0,0 +1,320 @@ +const db = require('../config/database'); + +// Get all app versions +exports.getAllAppVersions = async (req, res) => { + try { + const query = 'SELECT * FROM app_versions ORDER BY platform'; + const rows = await db.query(query); + + res.status(200).json({ + success: true, + data: rows + }); + } catch (error) { + console.error('Error fetching app versions:', error.message); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}; + +// Get app version by platform +exports.getAppVersionByPlatform = async (req, res) => { + try { + const { platform } = req.params; + + if (!['android', 'ios'].includes(platform)) { + return res.status(400).json({ + success: false, + error: 'Platform must be either "android" or "ios"' + }); + } + + const query = 'SELECT * FROM app_versions WHERE platform = ?'; + const rows = await db.query(query, [platform]); + + if (rows.length === 0) { + return res.status(404).json({ + success: false, + error: `No app version found for platform: ${platform}` + }); + } + + res.status(200).json({ + success: true, + data: rows[0] + }); + } catch (error) { + console.error('Error fetching app version:', error.message); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}; + +// Create new app version +exports.createAppVersion = async (req, res) => { + try { + const { + platform, + latest_version, + stable_version, + under_maintenance = false, + last_updated, + app_url + } = req.body; + + // Validate required fields + if (!platform || !latest_version || !stable_version || !last_updated || !app_url) { + return res.status(400).json({ + success: false, + error: 'All fields are required: platform, latest_version, stable_version, last_updated, app_url' + }); + } + + // Validate platform + if (!['android', 'ios'].includes(platform)) { + return res.status(400).json({ + success: false, + error: 'Platform must be either "android" or "ios"' + }); + } + + // Check if platform already exists + const existingQuery = 'SELECT id FROM app_versions WHERE platform = ?'; + const existingRows = await db.query(existingQuery, [platform]); + + if (existingRows.length > 0) { + return res.status(409).json({ + success: false, + error: `App version for platform "${platform}" already exists. Use PUT to update.` + }); + } + + // Insert new app version + const insertQuery = ` + INSERT INTO app_versions (platform, latest_version, stable_version, under_maintenance, last_updated, app_url) + VALUES (?, ?, ?, ?, ?, ?) + `; + + const result = await db.query(insertQuery, [ + platform, + latest_version, + stable_version, + under_maintenance, + last_updated, + app_url + ]); + + // Fetch the created record + const newVersion = await db.query('SELECT * FROM app_versions WHERE id = ?', [result.insertId]); + + res.status(201).json({ + success: true, + message: 'App version created successfully', + data: newVersion[0] + }); + } catch (error) { + console.error('Error creating app version:', error.message); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}; + +// Update app version by platform +exports.updateAppVersion = async (req, res) => { + try { + const { platform } = req.params; + const { + latest_version, + stable_version, + under_maintenance, + last_updated, + app_url + } = req.body; + + // Validate platform + if (!['android', 'ios'].includes(platform)) { + return res.status(400).json({ + success: false, + error: 'Platform must be either "android" or "ios"' + }); + } + + // Check if platform exists + const existingQuery = 'SELECT id FROM app_versions WHERE platform = ?'; + const existingRows = await db.query(existingQuery, [platform]); + + if (existingRows.length === 0) { + return res.status(404).json({ + success: false, + error: `No app version found for platform: ${platform}` + }); + } + + // Build update query dynamically based on provided fields + const updateFields = []; + const updateValues = []; + + if (latest_version !== undefined) { + updateFields.push('latest_version = ?'); + updateValues.push(latest_version); + } + if (stable_version !== undefined) { + updateFields.push('stable_version = ?'); + updateValues.push(stable_version); + } + if (under_maintenance !== undefined) { + updateFields.push('under_maintenance = ?'); + updateValues.push(under_maintenance); + } + if (last_updated !== undefined) { + updateFields.push('last_updated = ?'); + updateValues.push(last_updated); + } + if (app_url !== undefined) { + updateFields.push('app_url = ?'); + updateValues.push(app_url); + } + + if (updateFields.length === 0) { + return res.status(400).json({ + success: false, + error: 'At least one field must be provided for update' + }); + } + + updateValues.push(platform); + + const updateQuery = ` + UPDATE app_versions + SET ${updateFields.join(', ')} + WHERE platform = ? + `; + + await db.query(updateQuery, updateValues); + + // Fetch the updated record + const updatedVersion = await db.query('SELECT * FROM app_versions WHERE platform = ?', [platform]); + + res.status(200).json({ + success: true, + message: 'App version updated successfully', + data: updatedVersion[0] + }); + } catch (error) { + console.error('Error updating app version:', error.message); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}; + +// Delete app version by platform +exports.deleteAppVersion = async (req, res) => { + try { + const { platform } = req.params; + + // Validate platform + if (!['android', 'ios'].includes(platform)) { + return res.status(400).json({ + success: false, + error: 'Platform must be either "android" or "ios"' + }); + } + + // Check if platform exists + const existingQuery = 'SELECT id FROM app_versions WHERE platform = ?'; + const existingRows = await db.query(existingQuery, [platform]); + + if (existingRows.length === 0) { + return res.status(404).json({ + success: false, + error: `No app version found for platform: ${platform}` + }); + } + + // Delete the app version + const deleteQuery = 'DELETE FROM app_versions WHERE platform = ?'; + await db.query(deleteQuery, [platform]); + + res.status(200).json({ + success: true, + message: `App version for platform "${platform}" deleted successfully` + }); + } catch (error) { + console.error('Error deleting app version:', error.message); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}; + +// Get app version status (for client apps to check updates) +exports.getAppVersionStatus = async (req, res) => { + try { + const { platform, current_version } = req.query; + + // Validate platform + if (!platform || !['android', 'ios'].includes(platform)) { + return res.status(400).json({ + success: false, + error: 'Valid platform parameter is required (android or ios)' + }); + } + + // Get app version for the platform + const query = 'SELECT * FROM app_versions WHERE platform = ?'; + const rows = await db.query(query, [platform]); + + if (rows.length === 0) { + return res.status(404).json({ + success: false, + error: `No app version found for platform: ${platform}` + }); + } + + const appVersion = rows[0]; + + // Determine if update is needed + let update_required = false; + let update_type = null; + + if (current_version) { + // Simple version comparison (you might want to use a proper semver library) + if (current_version !== appVersion.latest_version) { + update_required = true; + update_type = 'latest'; + } else if (current_version !== appVersion.stable_version) { + update_required = true; + update_type = 'stable'; + } + } + + res.status(200).json({ + success: true, + data: { + platform: appVersion.platform, + latest_version: appVersion.latest_version, + stable_version: appVersion.stable_version, + under_maintenance: appVersion.under_maintenance, + last_updated: appVersion.last_updated, + app_url: appVersion.app_url, + update_required, + update_type + } + }); + } catch (error) { + console.error('Error fetching app version status:', error.message); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}; \ No newline at end of file diff --git a/src/migrations/20240610130000_create_app_versions.js b/src/migrations/20240610130000_create_app_versions.js new file mode 100644 index 0000000..6a68a28 --- /dev/null +++ b/src/migrations/20240610130000_create_app_versions.js @@ -0,0 +1,28 @@ +const db = require('../../config/database'); + +module.exports = { + async up() { + // Create app_versions table + await db.query(` + CREATE TABLE IF NOT EXISTS app_versions ( + id INT AUTO_INCREMENT PRIMARY KEY, + platform ENUM('android', 'ios') NOT NULL, + latest_version VARCHAR(20) NOT NULL, + stable_version VARCHAR(20) NOT NULL, + under_maintenance BOOLEAN DEFAULT FALSE, + last_updated DATE NOT NULL, + app_url VARCHAR(500) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY unique_platform (platform) + ) + `); + }, + + async down() { + // Drop app_versions table + await db.query(` + DROP TABLE IF EXISTS app_versions + `); + } +}; \ No newline at end of file diff --git a/src/routes/appVersioning.js b/src/routes/appVersioning.js new file mode 100644 index 0000000..cfd29dc --- /dev/null +++ b/src/routes/appVersioning.js @@ -0,0 +1,41 @@ +const express = require('express'); +const router = express.Router(); +const appVersionController = require('../controllers/appVersionController'); +const authMiddleware = require('../middlewares/authMiddleware'); +const roleMiddleware = require('../middlewares/roleMiddleware'); + +// Public route - Get app version status (for client apps) +router.get('/status', appVersionController.getAppVersionStatus); + +// Protected routes - Require authentication and admin role +// Get all app versions +router.get('/', + authMiddleware.authenticateToken, + appVersionController.getAllAppVersions +); + +// Get app version by platform +router.get('/:platform', + authMiddleware.authenticateToken, + appVersionController.getAppVersionByPlatform +); + +// Create new app version +router.post('/create', + authMiddleware.authenticateToken, + appVersionController.createAppVersion +); + +// Update app version by platform +router.put('/:platform', + authMiddleware.authenticateToken, + appVersionController.updateAppVersion +); + +// Delete app version by platform +router.delete('/:platform', + authMiddleware.authenticateToken, + appVersionController.deleteAppVersion +); + +module.exports = router; \ No newline at end of file