From 35996295ae9cebf58c2329f572c2fb5691fa3832 Mon Sep 17 00:00:00 2001 From: CodeAurelius0 Date: Wed, 24 Jun 2026 14:33:49 +0530 Subject: [PATCH] fix: resolve NGINX redirect issue and related server bugs (#6) - nginx: replace broken 'rewrite...redirect' with proxy_pass so NGINX internally forwards short-code requests to Node (port 7000) instead of sending the browser a 302 to an unreachable internal port - nginx: add proxy headers (Host, X-Real-IP, X-Forwarded-For) - nginx/shortener_default.conf: add missing short-code proxy_pass block and remove stray trailing 'u' character - server/routes/urlshorten.js: fix undeclared 'shortUrl' variable - server/routes/urlshorten.js: wrap cache calls in try/catch so Redis failures do not crash requests - server/routes/urlshorten.js: add Redis cache check on GET redirect - server/services/cache.js: log Redis errors instead of silently ignoring - server/config/constants.js: change errorUrl to localhost for local dev --- nginx/default.conf | 6 ++- nginx/shortener_default.conf | 48 +++++++++-------- server/config/constants.js | 2 +- server/routes/urlshorten.js | 99 +++++++++++++++++++++++++----------- server/services/cache.js | 4 +- 5 files changed, 105 insertions(+), 54 deletions(-) diff --git a/nginx/default.conf b/nginx/default.conf index bba9ca6..26e99d5 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -9,7 +9,11 @@ server { root /var/www/html/muhzi.com; } location ~* "^/[0-9a-z@]{5,15}$" { - rewrite ^/(.*)$ http://muhzi.com:7000/api/item/$1 redirect; + proxy_pass http://127.0.0.1:7000; + rewrite ^/(.*)$ /api/item/$1 break; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } diff --git a/nginx/shortener_default.conf b/nginx/shortener_default.conf index 67d6966..e5fefa6 100644 --- a/nginx/shortener_default.conf +++ b/nginx/shortener_default.conf @@ -1,21 +1,27 @@ -server { - listen 80; - listen [::]:80; - index index.html index.htm index.nginx-debian.html; - server_name shortener.muhzi.com; - root /var/www/html/shortener; - location / { - root /var/www/html/shortener; - } - location /v1/ { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header X-NginX-Proxy true; - proxy_pass http://127.0.0.1:7000/; - proxy_redirect off; - } - -} -u \ No newline at end of file +server { + listen 80; + listen [::]:80; + index index.html index.htm index.nginx-debian.html; + server_name shortener.muhzi.com; + root /var/www/html/shortener; + location / { + root /var/www/html/shortener; + } + location /v1/ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + proxy_pass http://127.0.0.1:7000/; + proxy_redirect off; + } + location ~* "^/[0-9a-z@]{5,15}$" { + proxy_pass http://127.0.0.1:7000; + rewrite ^/(.*)$ /api/item/$1 break; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + +} \ No newline at end of file diff --git a/server/config/constants.js b/server/config/constants.js index 8626492..c836f9a 100755 --- a/server/config/constants.js +++ b/server/config/constants.js @@ -1,4 +1,4 @@ module.exports = { mongoURI: "mongodb://localhost/url-shortner", - errorUrl: "https://muhzi.com/error" + errorUrl: "http://localhost/error" }; diff --git a/server/routes/urlshorten.js b/server/routes/urlshorten.js index 1392c4d..eefb3ed 100644 --- a/server/routes/urlshorten.js +++ b/server/routes/urlshorten.js @@ -5,55 +5,94 @@ const constants = require('../config/constants'); const shortCode = require('../middlewares/uniqueUrlCode'); const cache = require('../services/cache'); + module.exports = app => { + // GET /api/item/:code — redirect to the original URL app.get('/api/item/:code', async (req, res) => { const urlCode = req.params.code; - const item = await UrlShorten.findOne({ urlCode: urlCode }); - if (item) { - return res.redirect(item.originalUrl); - } else { + + try { + // 1. Check Redis cache first (avoids a DB round-trip on every redirect) + let urlData = await cache.getFromCache('urlCode', urlCode); + + if (!urlData) { + // 2. Fall back to MongoDB + urlData = await UrlShorten.findOne({ urlCode }).exec(); + + // 3. Populate cache for next time + if (urlData) { + cache.addToCache('urlCode', urlCode, urlData); + } + } + + if (urlData) { + return res.redirect(urlData.originalUrl); + } else { + return res.redirect(constants.errorUrl); + } + } catch (err) { + // Cache or DB error — still try a plain DB lookup so the redirect works + try { + const urlData = await UrlShorten.findOne({ urlCode }).exec(); + if (urlData) return res.redirect(urlData.originalUrl); + } catch (_) { /* intentional */ } return res.redirect(constants.errorUrl); } }); + // POST /api/item — shorten a URL app.post('/api/item', async (req, res) => { const { shortBaseUrl, originalUrl } = req.body; - if (validUrl.isUri(shortBaseUrl)) { - } else { + + if (!validUrl.isUri(shortBaseUrl)) { return res.status(404).json('Invalid Base Url format'); } + if (!validUrl.isUri(originalUrl)) { + return res.status(401).json('Invalid Original Url.'); + } + const updatedAt = new Date(); const queryOptions = { originalUrl }; - if (validUrl.isUri(originalUrl)) { + + try { let urlData; + + // Check Redis cache try { - // Find the item is in the cache urlData = await cache.getFromCache('originalUrl', JSON.stringify(queryOptions)); - if (!urlData) { - // Find the item is in the database - urlData = await UrlShorten.findOne(queryOptions).exec(); - } + } catch (_) { + // Redis unavailable — skip cache, fall through to DB + urlData = null; + } - if (urlData) { - res.status(200).json(urlData); - } else { - const urlCode = shortCode.generate(); - shortUrl = shortBaseUrl + '/' + urlCode; - const itemToBeSaved = { originalUrl, shortUrl, urlCode, updatedAt }; - - // Add the item to db - const item = new UrlShorten(itemToBeSaved); - await item.save(); - // Add the item to cache - cache.addToCache('originalUrl', JSON.stringify(queryOptions), itemToBeSaved); - res.status(200).json(itemToBeSaved); - } - } catch (err) { - res.status(401).json('Invalid User Id'); + if (!urlData) { + // Check MongoDB + urlData = await UrlShorten.findOne(queryOptions).exec(); } - } else { - return res.status(401).json('Invalid Original Url.'); + + if (urlData) { + return res.status(200).json(urlData); + } + + // Brand-new URL — generate a short code and persist + const urlCode = shortCode.generate(); + const shortUrl = shortBaseUrl + '/' + urlCode; // fixed: was undeclared + const itemToBeSaved = { originalUrl, shortUrl, urlCode, updatedAt }; + + const item = new UrlShorten(itemToBeSaved); + await item.save(); + + // Add to cache (best-effort; ignore Redis errors) + try { + cache.addToCache('originalUrl', JSON.stringify(queryOptions), itemToBeSaved); + cache.addToCache('urlCode', urlCode, itemToBeSaved); + } catch (_) { /* intentional */ } + + return res.status(200).json(itemToBeSaved); + } catch (err) { + console.error('POST /api/item error:', err); + return res.status(500).json('Internal server error'); } }); }; diff --git a/server/services/cache.js b/server/services/cache.js index 2d37c89..867b01b 100644 --- a/server/services/cache.js +++ b/server/services/cache.js @@ -6,7 +6,9 @@ const keys = require('../config/redis'); const client = redis.createClient(keys.redisUrl); client.hget = util.promisify(client.hget); -client.on('error', function(err) {}); +client.on('error', function(err) { + console.error('[Redis] Connection error:', err.message); +}); // eslint-disable-next-line const exec = mongoose.Query.prototype.exec;