From ec8bc98d501f28b56117229a182c0c2958d0b47a Mon Sep 17 00:00:00 2001 From: ibratabian17 Date: Mon, 9 Sep 2024 22:49:30 +0800 Subject: [PATCH] Improve Profiles & Leaderboard Function --- core/route/account.js | 402 ++++++++++++++++++++++++++++++-------- core/route/leaderboard.js | 226 +++++++++++---------- 2 files changed, 429 insertions(+), 199 deletions(-) diff --git a/core/route/account.js b/core/route/account.js index d9e805d..d8be082 100644 --- a/core/route/account.js +++ b/core/route/account.js @@ -1,133 +1,369 @@ -// core/route/account.js -//shit implementation, i need to fix it asap const fs = require("fs"); const axios = require("axios"); -const path = require('path') +const path = require('path'); const { getSavefilePath } = require('../helper'); -const { encrypt, decrypt } = require('../lib/encryptor') +const { encrypt, decrypt } = require('../lib/encryptor'); const secretKey = require('../../database/encryption.json').encrpytion.userEncrypt; const ubiwsurl = "https://public-ubiservices.ubi.com"; const prodwsurl = "https://prod.just-dance.com"; -var decryptedData = {} +let decryptedData = null; +let cachedLeaderboard = null; +let cachedDotw = null; +const LEADERBOARD_PATH = path.join(getSavefilePath(), 'leaderboard/leaderboard.json'); +const DOTW_PATH = path.join(getSavefilePath(), 'leaderboard/dotw.json'); + +// Helper function to load user data +function loadUserData(dataFilePath) { + if (!decryptedData) { // Load data from disk only if not already in memory + try { + const encryptedData = fs.readFileSync(dataFilePath, 'utf8'); + decryptedData = JSON.parse(decrypt(encryptedData, secretKey)); + } catch (err) { + console.log('[ACC] Unable to read user.json'); + console.log('[ACC] Is the key correct? Are the files corrupted?'); + console.log('[ACC] Ignore this message if this is the first run'); + console.log('[ACC] Resetting All User Data...'); + console.log(err); + decryptedData = {}; // Initialize as an empty object if file read fails + } + } + return decryptedData; +} + +function getWeekNumber() { + const now = new Date(); + const startOfWeek = new Date(now.getFullYear(), 0, 1); + const daysSinceStartOfWeek = Math.floor((now - startOfWeek) / (24 * 60 * 60 * 1000)); + return Math.ceil((daysSinceStartOfWeek + 1) / 7); +} + +// Helper function to save user data +function saveUserData(dataFilePath, data) { + const encryptedUserProfiles = encrypt(JSON.stringify(data), secretKey); + fs.writeFileSync(dataFilePath, encryptedUserProfiles); +} + +// Find user by ticket +function findUserFromTicket(ticket) { + return Object.values(decryptedData).find(profile => profile.ticket === ticket); +} + +// Find user by nickname +function findUserFromNickname(nickname) { + return Object.values(decryptedData).find(profile => profile.name === nickname); +} + +// Add a new user +function addUser(profileId, userProfile) { + decryptedData[profileId] = userProfile; + console.log(`[ACC] Added User With UUID: `, profileId); + const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`); + saveUserData(dataFilePath, decryptedData); +} + +function findUserFromTicket(ticket) { + return Object.values(decryptedData).find(profile => profile.ticket === ticket); +} + +// Helper function to read the leaderboard +function readLeaderboard(isDotw = false) { + if (!isDotw) { + if (!cachedLeaderboard) { + if (fs.existsSync(LEADERBOARD_PATH)) { + const data = fs.readFileSync(LEADERBOARD_PATH, 'utf-8'); + cachedLeaderboard = data + return JSON.parse(data); + } else { + return cachedLeaderboard + } + } + return {}; // Return empty object if file doesn't exist + } else { + if (!cachedDotw) { + if (fs.existsSync(DOTW_PATH)) { + const data = fs.readFileSync(DOTW_PATH, 'utf-8'); + cachedDotw = data + return JSON.parse(data); + } else { + return cachedDotw + } + } + return {}; // Return empty object if file doesn't exist + } +} + +// Helper function to save the leaderboard +function saveLeaderboard(leaderboard, isDotw = false) { + fs.writeFileSync(isDotw ? DOTW_PATH : LEADERBOARD_PATH, JSON.stringify(leaderboard, null, 2)); +} + +// Initialize routes exports.initroute = (app) => { // Endpoint to get profiles based on profileIds - app.get("/profile/v2/profiles", (req, res) => { - const ticket = req.header("Authorization") || ''; // Extract Authorization header - const profilesid = req.query.profileIds.split(','); // Split profileIds into an array - const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`); // Path to user data file - try { - if (decryptedData == {}) { - const encryptedData = fs.readFileSync(dataFilePath, 'utf8'); // Read encrypted user data - decryptedData = JSON.parse(decrypt(encryptedData, secretKey)); // Parse decrypted user data - } - } catch (err) { - decryptedData = {}; // Set empty object if data cannot be parsed - console.log('[ACC] Unable to read user.json') - console.log('[ACC] Is the key correct? are the files corrupted?') - console.log('[ACC] Ignore this message if this first run') - console.log('[ACC] Resetting All User Data...') - console.log(err) + app.get("/profile/v2/profiles", async (req, res) => { + const ticket = req.header("Authorization"); + const profileIds = req.query.profileIds.split(','); + const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`); + + // Load user data if not already loaded + if (!decryptedData) { + loadUserData(dataFilePath); } - // Map over profileIds to retrieve corresponding user profiles or create default profiles - const responseProfiles = profilesid.map(profileId => { - const userProfile = decryptedData[profileId]; // Get user profile based on profileId - if (userProfile) { - return { ...userProfile, ip: req.clientIp, ticket: ticket }; // Add IP to userProfile but not in the response + const responseProfiles = await Promise.all(profileIds.map(async (profileId) => { + let userProfile = decryptedData[profileId]; + + // If the profile is found in the local data + if (userProfile && Object.keys(userProfile).length >= 2) { + console.log(`[ACC] Account Found For: `, profileId); + return { ...userProfile, ip: req.clientIp, ticket: ticket }; } else { - const defaultProfile = { ip: req.clientIp, ticket: ticket }; // Create a default profile with IP address - decryptedData[profileId] = defaultProfile; // Add default profile to decrypted data - return {}; // Return an empty object (don't include defaultProfile in response) - } - }); + // If the profile is not found locally, fetch from external source + console.error(`[ACC] Asking Official Server For: `, profileId); + const url = `https://prod.just-dance.com/profile/v2/profiles?profileIds=${encodeURIComponent(profileId)}`; + try { + const profileResponse = await axios.get(url, { + headers: { + 'Host': 'prod.just-dance.com', + 'User-Agent': req.headers['user-agent'], + 'Accept': req.headers['accept'] || '*/*', + 'Accept-Language': req.headers['accept-language'] || 'en-us,en', + 'Authorization': ticket, + 'X-SkuId': req.headers['x-skuid'], + } + }); - const encryptedUserProfiles = encrypt(JSON.stringify(decryptedData), secretKey); // Stringify decrypted data - fs.writeFileSync(dataFilePath, encryptedUserProfiles); // Write updated data to file - res.send(responseProfiles); // Send response containing user profiles + // Assume the external response contains the profile as `profileData` + const profileData = profileResponse.data[profileId]; // Adjust according to the actual response format + if (profileData) { + const defaultProfile = { ...profileData, ip: req.clientIp, ticket: ticket }; + + // Add the fetched profile to local storage + addUser(profileId, defaultProfile); + + return defaultProfile; + } + } catch (error) { + console.error(`[ACC] Error fetching profile for ${profileId}:`, error.message); + addUser(profileId, { ip: req.clientIp, ticket: ticket }); + return {}; // If fetch fails, return an empty profile object + } + } + })); + + res.send(responseProfiles); }); - // Endpoint to update or create a user profile + app.post("/profile/v2/profiles", (req, res) => { - const ticket = req.header("Authorization"); // Extract Authorization header - const content = req.body; // Extract content from request body - const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`); // Path to user data file - try { - if (decryptedData == {}) { - const encryptedData = fs.readFileSync(dataFilePath, 'utf8'); // Read encrypted user data - decryptedData = JSON.parse(decrypt(encryptedData, secretKey)); // Parse decrypted user data - } - } catch (err) { - decryptedData = {}; // Set empty object if data cannot be parsed - console.log('[ACC] Unable to read user.json') - console.log('[ACC] Is the key correct? are the files corrupted?') - console.log('[ACC] Ignore this message if this first run') - console.log('[ACC] Resetting All User Data...') - console.log(err) - } - - // Find a matching profile based on name or IP address (only one profile) - // Check whether this user is a cracked game user - if (content.name === "ALI123") { - return res.status(400).send({ - error: "Cracked user is not allowed to use profiles" - }); // Send 400 status with error message + const ticket = req.header("Authorization"); + const content = req.body; + content.ticket = ticket; + const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`); + + // Load user data if not already loaded + if (!decryptedData) { + loadUserData(dataFilePath); } + + // Find matching profile by name or ticket const matchedProfileId = Object.keys(decryptedData).find(profileId => { - const userProfile = decryptedData[profileId]; // Get user profile based on profileId - return userProfile.name === content.name || userProfile.ticket === ticket || userProfile.ip === req.clientIp; // Check for name or IP match + const userProfile = decryptedData[profileId]; + return userProfile.name === content.name || userProfile.ticket === ticket; }); - + if (matchedProfileId) { - decryptedData[matchedProfileId] = content; // Update existing profile with posted content - const encryptedUserProfiles = encrypt(JSON.stringify(decryptedData), secretKey); // Stringify decrypted data - fs.writeFileSync(dataFilePath, encryptedUserProfiles); // Write updated data to file - res.send(encryptedUserProfiles); // Send updated encrypted data as response + const userProfile = decryptedData[matchedProfileId]; + let scoreChanged = false; + let updatedScoreMapName = ""; + let updatedScore = 0; + + // Merge new content into existing user profile, overriding or adding properties + Object.assign(userProfile, content); + + // Implement Leaderboard System for scores + if (content.scores) { + Object.keys(content.scores).forEach(mapName => { + const newScore = content.scores[mapName].highest; + const oldScore = (userProfile.scores && userProfile.scores[mapName]?.highest) || 0; + + if (newScore > oldScore) { + userProfile.scores[mapName] = { highest: newScore }; + scoreChanged = true; + updatedScoreMapName = mapName; + updatedScore = newScore; + console.log(`[ACC] New highest score for ${mapName}: ${newScore}`); + } + }); + } + + if (scoreChanged) { + let leaderboard = readLeaderboard(false); + if (!leaderboard[updatedScoreMapName]) { + leaderboard[updatedScoreMapName] = []; + } + + const currentScores = leaderboard[updatedScoreMapName]; + const existingEntryIndex = currentScores.findIndex(entry => entry.profileId === matchedProfileId); + + if (existingEntryIndex !== -1) { + // Update leaderboard entry if the new score is higher + if (currentScores[existingEntryIndex].score < updatedScore) { + currentScores[existingEntryIndex].score = updatedScore; + console.log(`[ACC] Updated leaderboard for map ${updatedScoreMapName}`); + } + } else { + // Add a new leaderboard entry + const newScoreEntry = { + __class: "LeaderboardEntry", + score: updatedScore, + profileId: matchedProfileId, + gameVersion: getGameVersion(req), + rank: userProfile.rank, + name: userProfile.name, + avatar: userProfile.avatar, + country: userProfile.country, + platformId: userProfile.platformId, + alias: userProfile.alias, + aliasGender: userProfile.aliasGender, + jdPoints: userProfile.jdPoints, + portraitBorder: userProfile.portraitBorder, + weekOptain: getWeekNumber() + }; + + currentScores.push(newScoreEntry); + leaderboard[updatedScoreMapName] = currentScores; + saveLeaderboard(leaderboard, false); + } + } + + // Save updated user profile data + decryptedData[matchedProfileId] = userProfile; + saveUserData(dataFilePath, decryptedData); + + res.send(decryptedData[matchedProfileId]); } else { - res.status(404).send("Profile not found."); // Send 404 status if profile not found + console.error("[ACC] Can't Find UUID: ", matchedProfileId); + res.status(404).send("Profile not found."); } }); + + app.post("/profile/v2/map-ended", async (req, res) => { + const ticket = req.header("Authorization"); + const clientIp = req.ip; + + try { + const mapList = req.body; + let leaderboard = readLeaderboard(true); // Load the current leaderboard data + + for (let song of mapList) { + core.updateMostPlayed(song.mapName); + + // Initialize the map in the leaderboard if it doesn't exist + if (!leaderboard[song.mapName]) { + leaderboard[song.mapName] = []; + } + + // Find the user profile + const profile = findUserFromTicket(ticket); + if (!profile) { + return res.send('1'); + } + + // Check if an entry for this profileId already exists + const currentScores = leaderboard[song.mapName]; + const existingEntryIndex = currentScores.findIndex(entry => entry.profileId === profile.profileId); + + if (existingEntryIndex !== -1) { + // Entry exists for this profile, update if the new score is higher + if (currentScores[existingEntryIndex].score < song.score) { + currentScores[existingEntryIndex].score = song.score; + console.log(`[LEADERBOARD] Updated score dotw list on map ${song.mapName}`); + } else { + return res.send('1'); // Do nothing if the new score is lower + } + } else { + // No existing entry for this profile, add a new one + const newScoreEntry = { + __class: "LeaderboardEntry", + score: song.score, + profileId: profile.profileId, + gameVersion: getGameVersion(req), + rank: profile.rank, + name: profile.name, + avatar: profile.avatar, + country: profile.country, + platformId: profile.platformId, + alias: profile.alias, + aliasGender: profile.aliasGender, + jdPoints: profile.jdPoints, + portraitBorder: profile.portraitBorder, + weekOptain: getWeekNumber() + }; + + currentScores.push(newScoreEntry); + leaderboard[song.mapName] = currentScores + console.log(`[LEADERBOARD] Added new score for ${profile.name} on map ${song.mapName}`); + } + } + + // Save the updated leaderboard back to the file + saveLeaderboard(leaderboard, true); + res.send(''); + + } catch (error) { + console.log(error); + res.status(200).send(''); // Keep sending response even in case of error + } + }); + + // Delete favorite map app.delete("/profile/v2/favorites/maps/:MapName", async (req, res) => { try { - var MapName = req.params.MapName; - var ticket = req.header("Authorization"); - var SkuId = req.header("X-SkuId"); - var response = await axios.delete( - prodwsurl + "/profile/v2/favorites/maps/" + MapName, - { - headers: { - "X-SkuId": SkuId, - Authorization: ticket, - }, - } - ); + const MapName = req.params.MapName; + const ticket = req.header("Authorization"); + const SkuId = req.header("X-SkuId"); + + const response = await axios.delete( + `${prodwsurl}/profile/v2/favorites/maps/${MapName}`, { + headers: { + "X-SkuId": SkuId, + Authorization: ticket, + }, + }); + res.send(response.data); } catch (error) { res.status(500).send(error.message); } }); + // Get profile sessions app.get("/v3/profiles/sessions", async (req, res) => { try { - var ticket = req.header("Authorization"); - var appid = req.header("Ubi-AppId"); - var response = await axios.get(ubiwsurl + "/v3/profiles/sessions", { + const ticket = req.header("Authorization"); + const appid = req.header("Ubi-AppId"); + + const response = await axios.get(`${ubiwsurl}/v3/profiles/sessions`, { headers: { "Content-Type": "application/json", "Ubi-AppId": appid, Authorization: ticket, }, }); + res.send(response.data); } catch (error) { res.status(500).send(error.message); } }); - app.post("/profile/v2/filter-players", function (request, response) { - response.send(["00000000-0000-0000-0000-000000000000"]); + // Endpoint to filter players + app.post("/profile/v2/filter-players", (req, res) => { + res.send(["00000000-0000-0000-0000-000000000000"]); }); } diff --git a/core/route/leaderboard.js b/core/route/leaderboard.js index dec52c4..f1da874 100644 --- a/core/route/leaderboard.js +++ b/core/route/leaderboard.js @@ -8,7 +8,9 @@ const core = { CloneObject: require('../helper').CloneObject, getSavefilePath: require('../helper').getSavefilePath, generateCarousel: require('../carousel/carousel').generateCarousel, generateSweatCarousel: require('../carousel/carousel').generateSweatCarousel, generateCoopCarousel: require('../carousel/carousel').generateCoopCarousel, updateMostPlayed: require('../carousel/carousel').updateMostPlayed } -const DOTW_PATH = path.join(core.getSavefilePath(), 'leaderboard/dotw/'); +const LEADERBOARD_PATH = path.join(core.getSavefilePath(), 'leaderboard/leaderboard.json'); +const DOTW_PATH = path.join(core.getSavefilePath(), 'leaderboard/dotw.json'); + const { getSavefilePath } = require('../helper'); const { encrypt, decrypt } = require('../lib/encryptor') @@ -32,34 +34,6 @@ function generateToolNickname() { } -function getProfileData(ticket, content, clientIp) { - const dataFilePath = path.join(getSavefilePath(), '/account/profiles/user.json'); - try { - if (Object.keys(decryptedData).length === 0) { - const encryptedData = fs.readFileSync(dataFilePath, 'utf8'); - decryptedData = JSON.parse(decrypt(encryptedData, secretKey)); - } - } catch (err) { - decryptedData = {}; - console.log('[ACC] Unable to read user.json'); - console.log('[ACC] Is the key correct? are the files corrupted?'); - console.log('[ACC] Ignore this message if this first run'); - console.log('[ACC] Resetting All User Data...'); - console.log(err); - } - - const matchedProfileId = Object.keys(decryptedData).find(profileId => { - const userProfile = decryptedData[profileId]; - return userProfile.ticket === ticket || userProfile.ip === clientIp; - }); - - if (matchedProfileId) { - return decryptedData[matchedProfileId]; - } else { - return false; - } -} - const getGameVersion = (req) => { const sku = req.header('X-SkuId') || "jd2019-pc-ww"; return sku.substring(0, 6) || "jd2019"; @@ -129,108 +103,128 @@ const initroute = (app) => { app.get("/leaderboard/v1/maps/:mapName/world", async (req, res) => { const { mapName } = req.params; - + let leaderboardData = { "__class": "LeaderboardList", "entries": [] }; - + try { - leaderboardData.entries.push( - { - "__class": "LeaderboardEntry_Online", - "profileId": "00000000-0000-0000-0000-000000000000", - "score": Math.floor(Math.random() * 1333) + 12000, - "name": generateToolNickname(), - "avatar": Math.floor(Math.random() * 100), - "country": Math.floor(Math.random() * 20), - "platformId": "e3", - "alias": 0, - "aliasGender": 0, - "jdPoints": 0, - "portraitBorder": 0, - "mapName": mapName - }); + // Read the leaderboard file + const leaderboardFilePath = LEADERBOARD_PATH; + if (fs.existsSync(leaderboardFilePath)) { + const data = fs.readFileSync(leaderboardFilePath, 'utf-8'); + const leaderboard = JSON.parse(data); + + // Check if there are entries for the mapName + if (leaderboard[mapName]) { + // Sort the leaderboard entries by score in descending order + const sortedEntries = leaderboard[mapName].sort((a, b) => b.score - a.score); + + leaderboardData.entries = sortedEntries.map(entry => ({ + "__class": "LeaderboardEntry_Online", + "profileId": entry.profileId, + "score": entry.score, + "name": entry.name, + "avatar": entry.avatar, + "country": entry.country, + "platformId": entry.platformId, + "alias": entry.alias, + "aliasGender": entry.aliasGender, + "jdPoints": entry.jdPoints, + "portraitBorder": entry.portraitBorder, + "mapName": mapName + })); + } + } + res.json(leaderboardData); } catch (error) { console.error("Error:", error.message); res.status(500).send("Internal Server Error"); } }); - - app.post("/profile/v2/map-ended", async (req, res) => { - const ticket = req.header("Authorization"); - const clientIp = req.ip; - try { - const mapList = req.body; - for (let song of mapList) { - core.updateMostPlayed(song.mapName); - - const dotwFilePath = path.join(DOTW_PATH, `${song.mapName}.json`); - if (fs.existsSync(dotwFilePath)) { - const readFile = fs.readFileSync(dotwFilePath, 'utf-8'); - const JSONParFile = JSON.parse(readFile); - if (JSONParFile.score > song.score) { - return res.send('1'); - } - } else { - const profiljson1 = await getProfileData(ticket, song, clientIp); - if (!profiljson1) { - return res.send('1') - } - - const jsontodancerweek = { - __class: "DancerOfTheWeek", - score: song.score, - profileId: profiljson1.profileId, - gameVersion: getGameVersion(req), - rank: profiljson1.rank, - name: profiljson1.name, - avatar: profiljson1.avatar, - country: profiljson1.country, - platformId: profiljson1.platformId, - alias: profiljson1.alias, - aliasGender: profiljson1.aliasGender, - jdPoints: profiljson1.jdPoints, - portraitBorder: profiljson1.portraitBorder, - }; - - fs.writeFileSync(dotwFilePath, JSON.stringify(jsontodancerweek, null, 2)); - console.log(`DOTW file for ${song.mapName} created!`); - res.send(''); - } - } - } catch (error) { - console.log(error) - res.status(200).send(''); //keep send - } - }); + app.get("/leaderboard/v1/maps/:map/dancer-of-the-week", (req, res) => { - const dotwFilePath = path.join(DOTW_PATH, `${req.params.map}.json`); - if (fs.existsSync(dotwFilePath)) { - const readFile = fs.readFileSync(dotwFilePath, 'utf-8'); - res.send(readFile); - } else { - res.setHeader('Content-Type', 'application/json; charset=utf-8'); - res.setHeader('Access-Control-Allow-Origin', '*'); - res.send({ - "__class": "DancerOfTheWeek", - "profileId": "00000000-0000-0000-0000-000000000000", - "score": 69, - "gameVersion": "jd2019", - "rank": 1, - "name": "NO DOTW", - "avatar": 1, - "country": 0, - "platformId": "3935074714266132752", - "alias": 0, - "aliasGender": 0, - "jdPoints": 0, - "portraitBorder": 0 - }); + const { map } = req.params; + + // Path to leaderboard file + const leaderboardFilePath = DOTW_PATH; + + try { + if (fs.existsSync(leaderboardFilePath)) { + const data = fs.readFileSync(leaderboardFilePath, 'utf-8'); + const leaderboard = JSON.parse(data); + + // Check if the map exists in the leaderboard + if (leaderboard[map] && leaderboard[map].length > 0) { + // Find the highest score entry for this map + const highestEntry = leaderboard[map].reduce((max, entry) => entry.score > max.score ? entry : max); + + const dancerOfTheWeek = { + "__class": "DancerOfTheWeek", + "profileId": highestEntry.profileId, + "score": highestEntry.score, + "gameVersion": highestEntry.gameVersion || "jd2020", + "rank": highestEntry.rank, // Since it's the highest, assign rank 1 + "name": highestEntry.name, + "avatar": highestEntry.avatar, + "country": highestEntry.country, + "platformId": highestEntry.platformId, + "alias": highestEntry.alias, + "aliasGender": highestEntry.aliasGender, + "jdPoints": highestEntry.jdPoints, + "portraitBorder": highestEntry.portraitBorder + }; + + res.json(dancerOfTheWeek); + } else { + // If no entries for the map, return default "NO DOTW" response + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.setHeader('Access-Control-Allow-Origin', '*'); + res.json({ + "__class": "DancerOfTheWeek", + "profileId": "00000000-0000-0000-0000-000000000000", + "score": 69, + "gameVersion": "jd2019", + "rank": 1, + "name": "NO DOTW", + "avatar": 1, + "country": 0, + "platformId": "3935074714266132752", + "alias": 0, + "aliasGender": 0, + "jdPoints": 0, + "portraitBorder": 0 + }); + } + } else { + // If leaderboard file does not exist, return default "NO DOTW" response + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.setHeader('Access-Control-Allow-Origin', '*'); + res.json({ + "__class": "DancerOfTheWeek", + "profileId": "00000000-0000-0000-0000-000000000000", + "score": 69, + "gameVersion": "jd2019", + "rank": 1, + "name": "NO DOTW", + "avatar": 1, + "country": 0, + "platformId": "3935074714266132752", + "alias": 0, + "aliasGender": 0, + "jdPoints": 0, + "portraitBorder": 0 + }); + } + } catch (error) { + console.error("Error:", error.message); + res.status(500).send("Internal Server Error"); } }); + }; module.exports = { initroute };