const fs = require("fs"); const path = require("path"); const { execSync } = require("child_process"); class AdminSync { constructor() { this.mainRepoPath = "temp-main"; this.adminRepoPath = process.cwd(); this.mainRepoUrl = "https://git.kh3group.com/george/website.git"; this.syncLog = []; } async syncToMain() { console.log("๐Ÿš€ Starting sync to main site..."); this.log("Starting sync process"); try { // Step 1: Clone main repository await this.cloneMainRepo(); // Step 2: Sync products.json await this.syncProducts(); // Step 3: Sync images await this.syncImages(); // Step 4: Validate changes await this.validateChanges(); // Step 5: Commit and push await this.commitAndPush(); // Step 6: Cleanup this.cleanup(); console.log("โœ… Successfully synced to main site!"); console.log("๐ŸŒ Your changes should be live shortly."); this.log("Sync completed successfully"); } catch (error) { console.error("โŒ Sync failed:", error.message); this.log(`Sync failed: ${error.message}`, "error"); this.cleanup(); process.exit(1); } } async cloneMainRepo() { console.log("๐Ÿ“ฅ Cloning main repository..."); this.log("Cloning main repository"); if (fs.existsSync(this.mainRepoPath)) { execSync(`rm -rf ${this.mainRepoPath}`); } try { execSync(`git clone ${this.mainRepoUrl} ${this.mainRepoPath}`, { stdio: "pipe", }); execSync(`cd ${this.mainRepoPath} && git checkout main`, { stdio: "pipe", }); this.log("Main repository cloned successfully"); } catch (error) { throw new Error(`Failed to clone repository: ${error.message}`); } } async syncProducts() { console.log("๐Ÿ“ฆ Syncing products.json..."); this.log("Syncing products data"); const adminProductsPath = path.join( this.adminRepoPath, "data/products.json" ); const mainProductsPath = path.join(this.mainRepoPath, "data/products.json"); if (!fs.existsSync(adminProductsPath)) { throw new Error("products.json not found in admin repo"); } // Ensure main data directory exists const mainDataDir = path.dirname(mainProductsPath); if (!fs.existsSync(mainDataDir)) { fs.mkdirSync(mainDataDir, { recursive: true }); } // Copy products.json fs.copyFileSync(adminProductsPath, mainProductsPath); console.log(" โœ… Products synced"); this.log("Products data synced successfully"); } async syncImages() { console.log("๐Ÿ–ผ๏ธ Syncing images..."); this.log("Syncing product images"); const adminImagesPath = path.join( this.adminRepoPath, "assets/images/products" ); const mainImagesPath = path.join( this.mainRepoPath, "assets/images/products" ); // Create main images directory if it doesn't exist if (!fs.existsSync(mainImagesPath)) { fs.mkdirSync(mainImagesPath, { recursive: true }); } let imagesProcessed = 0; let imagesRemoved = 0; if (fs.existsSync(adminImagesPath)) { // Get all files in admin images directory const adminImages = fs.readdirSync(adminImagesPath); const mainImages = fs.existsSync(mainImagesPath) ? fs.readdirSync(mainImagesPath) : []; // Copy new/updated images for (const image of adminImages) { const srcPath = path.join(adminImagesPath, image); const destPath = path.join(mainImagesPath, image); if (fs.statSync(srcPath).isFile()) { fs.copyFileSync(srcPath, destPath); console.log(` ๐Ÿ“ธ Copied: ${image}`); imagesProcessed++; } } // Remove orphaned images (in main but not in admin) for (const image of mainImages) { const adminImagePath = path.join(adminImagesPath, image); if (!fs.existsSync(adminImagePath)) { fs.unlinkSync(path.join(mainImagesPath, image)); console.log(` ๐Ÿ—‘๏ธ Removed: ${image}`); imagesRemoved++; } } } console.log( ` โœ… Images synced (${imagesProcessed} copied, ${imagesRemoved} removed)` ); this.log( `Images synced: ${imagesProcessed} copied, ${imagesRemoved} removed` ); } async validateChanges() { console.log("๐Ÿ” Validating changes..."); this.log("Validating synced data"); // Validate JSON const productsPath = path.join(this.mainRepoPath, "data/products.json"); if (!fs.existsSync(productsPath)) { throw new Error("products.json not found after sync"); } let productsData; try { productsData = JSON.parse(fs.readFileSync(productsPath, "utf8")); } catch (error) { throw new Error("Invalid JSON in products.json"); } if (!productsData.products || !Array.isArray(productsData.products)) { throw new Error("Invalid products.json structure"); } // Check if all referenced images exist let missingImages = 0; for (const product of productsData.products) { if (product.images) { for (const imagePath of product.images) { const fullPath = path.join(this.mainRepoPath, imagePath); if (!fs.existsSync(fullPath)) { console.warn(`โš ๏ธ Warning: Image not found: ${imagePath}`); missingImages++; } } } } if (missingImages > 0) { console.warn(`โš ๏ธ Found ${missingImages} missing images`); } console.log(" โœ… Validation passed"); this.log(`Validation passed (${missingImages} missing images)`); } async commitAndPush() { console.log("๐Ÿ’พ Committing and pushing changes..."); this.log("Committing and pushing changes"); try { // Add all changes execSync(`cd ${this.mainRepoPath} && git add .`, { stdio: "pipe" }); // Check if there are changes to commit try { execSync(`cd ${this.mainRepoPath} && git diff --cached --quiet`, { stdio: "pipe", }); console.log(" โ„น๏ธ No changes to commit"); this.log("No changes to commit"); return; } catch (error) { // There are changes, proceed with commit } // Commit changes const commitMessage = `Update products and images from admin dashboard - ${new Date().toISOString()}`; execSync(`cd ${this.mainRepoPath} && git commit -m "${commitMessage}"`, { stdio: "pipe", }); // Push to main branch execSync(`cd ${this.mainRepoPath} && git push origin main`, { stdio: "pipe", }); console.log(" โœ… Changes pushed to main repository"); this.log("Changes pushed successfully"); } catch (error) { throw new Error(`Failed to commit/push: ${error.message}`); } } cleanup() { console.log("๐Ÿงน Cleaning up..."); this.log("Cleaning up temporary files"); if (fs.existsSync(this.mainRepoPath)) { execSync(`rm -rf ${this.mainRepoPath}`); } } log(message, type = "info") { const timestamp = new Date().toISOString(); const logEntry = { timestamp, type, message, }; this.syncLog.push(logEntry); // Save log to file const logPath = path.join(this.adminRepoPath, "sync-log.json"); fs.writeFileSync(logPath, JSON.stringify(this.syncLog, null, 2)); } // Method to get sync history getSyncHistory() { const logPath = path.join(this.adminRepoPath, "sync-log.json"); if (fs.existsSync(logPath)) { return JSON.parse(fs.readFileSync(logPath, "utf8")); } return []; } } // Run sync if called directly if (require.main === module) { const sync = new AdminSync(); sync.syncToMain(); } module.exports = AdminSync;