Skip to content

Comments

Test branch#43

Open
TheBlueHaze369 wants to merge 9 commits intotinkerhub:mainfrom
TheBlueHaze369:test_branch
Open

Test branch#43
TheBlueHaze369 wants to merge 9 commits intotinkerhub:mainfrom
TheBlueHaze369:test_branch

Conversation

@TheBlueHaze369
Copy link

@TheBlueHaze369 TheBlueHaze369 commented Feb 14, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Launched Sanchaari, a personalized travel companion app featuring user login and authentication.
    • Added trip planning with AI-powered itinerary generation based on destination, duration, and travel priorities.
    • Introduced in-trip mode to discover nearby services and attractions using location data.
    • Enabled saving and retrieving past itineraries for future reference.

@coderabbitai
Copy link

coderabbitai bot commented Feb 14, 2026

📝 Walkthrough

Walkthrough

Establishes the complete initial architecture for Sanchaari, a personalized travel planning application. Introduces a full-stack setup with a frontend UI (HTML, CSS, JavaScript), Express.js backend with SQLite database, authentication system, itinerary CRUD APIs, and project configuration files.

Changes

Cohort / File(s) Summary
Project Configuration
.gitignore, package.json, README.md
Configures project metadata, dependencies (Express, SQLite, JWT, logging), scripts, and development details. Adds standard gitignore entries for Node.js artifacts, environment files, and editor configs. Updates README with project name "Sanchaari", team info, and feature descriptions.
Frontend Application
index.html, script9.js, style9.css
Implements a complete single-page application with login, mode selection, planning form, and itinerary result views. Client-side handles authentication, mode routing, multi-city itinerary generation with fallback logic, saved itinerary management, and geolocation-based nearby services via Google Maps integration. CSS provides glassmorphic UI with animations and responsive layouts.
Backend Server Bootstrap
src/index.js, src/server.js
Configures Express application with middleware (CORS, compression, logging), static asset serving, API route mounting at /api, error handling, and server startup routine with database initialization.
Configuration & Logging
src/config.js, src/logger.js
Centralizes environment variable management (port, JWT secret, rate limits, database file path) with sensible defaults. Sets up Winston logger with JSON formatting, file transports (error.log, combined.log), and conditional console logging for non-production environments.
Database Layer
src/db/index.js, src/db/init.js
Provides lazy-initialized SQLite database connection via getDb(). Implements schema migrations creating users and itineraries tables with constraints and indexes for authenticated CRUD operations.
Authentication Middleware & Routes
src/middleware/auth.js, src/routes/auth.js
Implements JWT-based authentication middleware that validates Bearer tokens from request headers. Provides registration and login endpoints with input validation, password hashing via bcrypt, token generation (30-day expiry), and error handling for duplicate usernames and invalid credentials.
Itinerary API Routes
src/routes/index.js, src/routes/itineraries.js
Establishes central router mounting auth and itinerary sub-routers with health check endpoint. Implements full CRUD for itineraries: POST creates with server-side generation using city data (predefined for Kochi, procedurally generated for unknown cities), GET lists user itineraries, single retrieval includes access control, DELETE removes with ownership verification. Validates input (destination, days 1–30, priorities) and persists to SQLite.
Error Handling Middleware
src/middleware/errorHandler.js
Centralizes HTTP error response handling, logs errors via logger, derives status codes, and returns standardized JSON error responses.

Sequence Diagram(s)

sequenceDiagram
    participant User as User / Browser
    participant Frontend as Frontend (JS)
    participant Backend as Express Backend
    participant Database as SQLite DB
    participant Auth as JWT Auth

    User->>Frontend: Enter username/password
    Frontend->>Backend: POST /api/auth/login
    Backend->>Database: Query user by username
    Database-->>Backend: User record
    Backend->>Auth: Verify password hash
    Auth-->>Backend: Hash match confirmed
    Backend->>Auth: Generate JWT token
    Auth-->>Backend: Token (30 days)
    Backend-->>Frontend: Return token + user data
    Frontend->>Frontend: Store token locally
    Frontend->>User: Show mode selection page
Loading
sequenceDiagram
    participant User as User / Browser
    participant Frontend as Frontend (JS)
    participant Backend as Express Backend
    participant Database as SQLite DB
    participant CityData as City Data Generator

    User->>Frontend: Fill destination, days, priorities
    Frontend->>Backend: POST /api/itineraries (with JWT token)
    Backend->>Auth: Validate JWT token
    Auth-->>Backend: Token valid, user identified
    Backend->>CityData: getOrGenerateCityData(destination)
    CityData-->>Backend: City spots (predefined or generated)
    Backend->>CityData: generateItineraryItems(destination, days, priorities)
    CityData-->>Backend: Day-by-day activities
    Backend->>Database: Insert itinerary record
    Database-->>Backend: Itinerary ID + metadata
    Backend-->>Frontend: Return generated itinerary with items
    Frontend->>User: Display itinerary results with Google Maps
Loading
sequenceDiagram
    participant User as User / Browser
    participant Frontend as Frontend (JS)
    participant Geolocation as Browser Geolocation
    participant Maps as Google Maps API
    participant Backend as Express Backend

    User->>Frontend: Click "Find Nearby" (Food/Tourist/Shopping)
    Frontend->>Geolocation: Request user location
    Geolocation-->>Frontend: Latitude/Longitude
    Frontend->>Maps: Update iframe with location + category query
    Maps-->>Frontend: Render map with nearby locations
    Frontend->>Frontend: Display category name & "Open in Maps" link
    Frontend->>User: Show interactive map with nearby services
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A travel app hops into place,
With plans and maps and every trace,
From login forms to journeys bright,
Sanchaari guides through day and night!
SQLite stores what rabbits dream—
Adventures await this startup team! ✨🗺️

🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Test branch' is vague and generic, using a non-descriptive term that doesn't convey meaningful information about the substantial changes in the changeset. Use a descriptive title that summarizes the main changes, such as 'Add Sanchaari travel planning application with backend API and frontend UI' or similar.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🤖 Fix all issues with AI agents
In `@index.html`:
- Around line 97-111: The three select elements (priority-1, priority-2,
priority-3) currently allow duplicate categories; implement client-side logic to
enforce unique selections by listening for change events on these IDs and either
(a) disable the selected option value in the other two selects or (b) rebuild
their option lists to exclude already-chosen values, and also validate on form
submission to prevent duplicates; update the event handlers to re-enable options
when a selection is changed and ensure initial state handles defaults.

In `@README.md`:
- Line 64: Typo: the README sentence contains "assits" instead of "assists";
update the sentence fragment "we created a website that assits the passenger..."
to use the correct spelling "assists" so it reads "we created a website that
assists the passenger..." (keep the rest of the sentence unchanged).
- Line 7: The repository is missing the referenced .env.example file; add a new
.env.example file to the repo that lists all required environment variables (at
minimum JWT_SECRET, PORT, DB_PATH) with placeholder/example values and brief
comments, so developers can copy it to .env as instructed in README; ensure the
filename matches exactly ".env.example" and include any other env keys used by
startup code (search for usages of process.env.JWT_SECRET, process.env.PORT,
process.env.DB_PATH, etc.) to document them in the example file.

In `@script9.js`:
- Around line 25-46: The apiRequest function currently re-throws any exception
from fetch/raw network failures without a consistent shape, so callers that read
err.status/err.body get undefined; modify apiRequest to normalize all errors
into a consistent object (e.g., { status: 0, body: <error message or original
error> } for network/fetch exceptions) and still preserve HTTP error objects
thrown when res.ok is false; specifically update the catch block in apiRequest
(which wraps fetch/API_BASE) to detect when no Response was received and throw a
normalized error with status and body properties (and include the original error
under a field like cause) so callers that check err.status/err.body behave
deterministically.
- Around line 383-389: The code builds HTML via string interpolation into the
listHtml constant using it.destination which allows XSS; sanitize or escape the
destination before inserting into the template (or avoid innerHTML by creating
DOM nodes). Modify the mapping that produces listHtml (the arrow function that
builds the template string) to escape dangerous characters in it.destination (or
use a helper escapeHtml function) and ensure the resulting string is safe when
assigned to innerHTML, keeping viewItinerary("${it.id}") unchanged but ensure id
is also validated/escaped if used in HTML context.
- Around line 67-97: The code currently treats any 401 in the initial login flow
as "user not found" and auto-attempts registration; change this to avoid
misfires by either (A) checking the server error payload (err.body?.error?.code
or err.body?.error?.message) and only invoking the registration branch when the
error explicitly indicates user-not-found, or (B) removing the automatic
registration branch entirely and instead surface a clear UI pathway to call
apiRequest('/auth/register', 'POST', ...) from an explicit registration action;
update the catch block around the initial login (the try/catch that references
apiRequest, authToken, localStorage, currentUser,
document.getElementById('display-username'), and showSection) so it no longer
attempts registration on every 401 unless the error body proves the user is
missing.
- Around line 349-376: renderItinerary currently injects server-provided fields
directly into a template string assigned to container.innerHTML (e.g.,
i.morning.place, i.e., i.evening.type, i.afternoon.place), which is an XSS
vector; change the implementation to build DOM nodes and set user data with
textContent (or run a proper sanitizer) instead of string
interpolation—specifically, stop using container.innerHTML += dayHTML, create
the outer day element (matching the structure in dayHTML), fill titles and
place/type values via element.textContent for i.day, i.morning.type,
i.morning.place, i.afternoon.type, i.afternoon.place, i.evening.type,
i.evening.place, and append the element to container to eliminate executing any
HTML in server data.

In `@src/config.js`:
- Line 9: The current jwtSecret fallback ('change_this_in_production') is
insecure: update the config logic that builds jwtSecret so that if
process.env.NODE_ENV === 'production' and process.env.JWT_SECRET is falsy the
app throws an Error (or exits) with a clear message instead of silently using
the hardcoded default; keep a non-production fallback only for dev/test. Modify
the jwtSecret assignment in src/config.js to validate process.env.JWT_SECRET and
raise an error in production, and ensure any code that imports this config
(jwtSecret) will fail fast on startup when misconfigured.

In `@src/db/index.js`:
- Around line 11-17: The current getDb() caches the raw open() promise in
dbPromise so if open() rejects the rejected promise is permanently stored;
change getDb() to assign dbPromise = open(...) then await it inside a try/catch
(use await dbPromise), and on catch reset dbPromise = null before rethrowing the
error so subsequent calls will retry; reference the getDb function, the
dbPromise variable, and the open(...) call when making the change.

In `@src/db/init.js`:
- Around line 15-24: Add a FOREIGN KEY constraint to the itineraries table
creation so itineraries.user_id references users(id) (e.g., ALTER the CREATE
TABLE itineraries statement to include "FOREIGN KEY(user_id) REFERENCES
users(id) ON DELETE CASCADE" or equivalent) to prevent orphaned rows, and enable
SQLite foreign key enforcement by executing PRAGMA foreign_keys = ON after
opening the DB in the getDb function (where dbPromise is created) in
src/db/index.js so the constraint is enforced at runtime.

In `@src/index.js`:
- Line 28: The current call app.use(express.static(process.cwd())) exposes the
entire project root (including .env, DB, config and source); change the static
middleware to serve only a dedicated public assets directory (e.g., replace the
express.static(process.cwd()) usage in your server bootstrap where app.use(...)
is called with a static serve pointing to a single public folder), move all
frontend assets (index.html, scripts, styles) into that folder, and ensure
sensitive files (env, DB, config) are excluded from served content and not
located under that public directory.

In `@src/middleware/errorHandler.js`:
- Around line 3-11: The errorHandler middleware currently returns err.message
for all errors which can leak internals for 5xx responses; update errorHandler
to always log the full error via logger.error(err) but when computing the JSON
response use a generic message (e.g., "Internal Server Error") if err.status is
500 or >=500 (or if err.expose is false), and only include err.message for
client/expected errors (status < 500 or an explicit expose flag). Keep using the
existing symbols errorHandler, logger.error(err), err.status, and
res.status(...).json(...) so the change simply selects the response message
based on err.status/err.expose while preserving full logging.

In `@src/routes/auth.js`:
- Line 43: The code uses the blocking bcrypt.compareSync call (const ok =
bcrypt.compareSync(password, row.password)); replace it with the non-blocking
async bcrypt.compare: make the enclosing handler function async (or return a
Promise), await bcrypt.compare(password, row.password), and handle errors;
update the variable 'ok' to receive the awaited result and proceed with the same
success/failure flow (including early returns) so the event loop is not blocked.
- Line 19: Replace the blocking bcrypt.hashSync call with the asynchronous
bcrypt.hash in the auth route: make the route handler async (or return a
Promise), call await bcrypt.hash(password, 10) instead of
bcrypt.hashSync(password, 10), and handle errors with try/catch so failures to
hash are caught and logged/responded to rather than crashing the request; update
any references to the synchronous "hashed" assignment accordingly (e.g., where
"hashed" is used to create the user or compare later).

In `@src/routes/itineraries.js`:
- Around line 8-12: The priorities array in createSchema currently allows any
three strings, causing generateItineraryItems to receive invalid keys and
produce generic placeholders; update createSchema's priorities validation to
restrict values to the known activity categories used by generateItineraryItems
(e.g., "Food Spots", "Tourist Spots", "Shopping" — or whatever exact category
strings generateItineraryItems expects) by replacing Joi.string() with
Joi.string().valid(...) inside the priorities array schema while keeping
.length(3). Ensure the valid list matches the keys used in
generateItineraryItems so invalid submissions are rejected.

In `@src/server.js`:
- Around line 13-16: app.listen's bind errors are emitted on the returned server
object, not thrown into the surrounding try/catch; change the call to capture
the server handle (e.g., const server = app.listen(port, () => {
logger.info(...); })) and attach an error listener on that handle
(server.on('error', err => { logger.error('Server failed to start', err); handle
specific cases like err.code === 'EADDRINUSE' and exit or retry as appropriate
})), ensuring you log the real error details rather than relying only on the
callback.

In `@style9.css`:
- Around line 107-117: The .page-section transition won't run because switching
from display: none to display: flex prevents CSS transitions; update the
approach so the element stays in layout and only visibility/opacity/transform
change: keep .page-section as display: flex (or block) always, change the
inactive state to visibility: hidden; pointer-events: none; opacity: 0;
transform: translateY(20px); and have .page-section.active set visibility:
visible; pointer-events: auto; opacity: 1; transform: translateY(0) so the
opacity/transform transitions run; alternatively, if you must toggle display,
modify the JavaScript that adds .page-section.active (or sets display) to first
set display: flex, then add .active on the next animation frame (using
requestAnimationFrame or a minimal setTimeout) so the transition can start.
🧹 Nitpick comments (11)
src/logger.js (1)

13-16: Consider adding log rotation to prevent unbounded disk usage.

File transports write to logs/error.log and logs/combined.log without any size limit or rotation. In a long-running process these files will grow indefinitely. Consider using winston-daily-rotate-file or an external log rotation mechanism.

package.json (1)

1-32: LGTM — dependencies are appropriate for the described stack.

One minor suggestion: consider adding an "engines" field to pin the minimum Node.js version, which helps avoid compatibility surprises across environments.

style9.css (1)

46-49: Stylelint: rename keyframe to kebab-case.

Static analysis flags panZoom as violating the keyframes-name-pattern rule. Rename to pan-zoom and update the reference on line 30.

Proposed fix
-        `@keyframes` panZoom {
+        `@keyframes` pan-zoom {

And on line 30:

-            animation: panZoom 30s infinite alternate;
+            animation: pan-zoom 30s infinite alternate;
src/db/init.js (1)

2-2: Unused path import (effectively).

path is only used on line 36 as path.resolve() which just prints the current working directory — not the actual database file path. Consider removing the import and printing the real DB path from your config instead.

src/middleware/auth.js (1)

10-16: Consider specifying allowed algorithms in jwt.verify.

Without an explicit algorithms option, the verification relies on the library's defaults. Pinning to the expected signing algorithm (e.g., HS256) is a defense-in-depth measure against algorithm-confusion attacks.

🔒 Suggested fix
-    const payload = jwt.verify(token, config.jwtSecret);
+    const payload = jwt.verify(token, config.jwtSecret, { algorithms: ['HS256'] });
src/index.js (1)

12-12: cors() with no options allows all origins.

This is fine for local development, but in production you should restrict allowed origins to prevent cross-origin abuse.

index.html (1)

8-8: Tailwind CSS CDN is not intended for production.

The Tailwind CDN build includes the entire framework (~300KB+) and is explicitly meant for development/prototyping only. For production, use the Tailwind CLI or PostCSS plugin to generate a purged, minified build.

script9.js (1)

330-331: innerHTML += inside a loop re-parses the entire container on each iteration.

This is O(n²) in the number of days. Build the full HTML string first (or use DocumentFragment / insertAdjacentHTML), then assign innerHTML once. The same applies to renderItinerary at line 374.

src/routes/itineraries.js (3)

59-84: Generation logic produces repetitive itineraries for longer trips.

Since pick() cycles with (i + offset) % list.length and lists have only 5–6 entries, trips longer than ~6 days will repeat the same places. Additionally, the evening activity category never rotates (only morning/afternoon swap on even days). This is acceptable for an MVP, but worth noting for future improvement.


106-115: No pagination on the list endpoint.

The query fetches all itineraries for a user with no LIMIT/OFFSET. For users with many itineraries, this could return a large payload (especially since items contains full day-by-day data). Consider adding pagination or at minimum a reasonable default limit.


96-97: Line length readability improvement (optional).

While both spread arguments and array form are valid for db.run(), this line is quite long. If readability is a concern, consider breaking it across multiple lines while preserving the established codebase pattern of spread arguments:

Optional improvement
-    await db.run('INSERT INTO itineraries (id, user_id, destination, days, priorities, items, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)', id, req.user.id, value.destination, value.days, JSON.stringify(value.priorities), JSON.stringify(items), createdAt);
+    await db.run(
+      'INSERT INTO itineraries (id, user_id, destination, days, priorities, items, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)',
+      id, req.user.id, value.destination, value.days, JSON.stringify(value.priorities), JSON.stringify(items), createdAt
+    );

Note: The codebase consistently uses spread arguments for all db.run() calls rather than array form.

Comment on lines +97 to +111
<select id="priority-1" class="input-field w-full px-4 py-2 rounded-lg cursor-pointer">
<option value="Tourist Spots">Tourist Spots (Scenic)</option>
<option value="Food Spots">Food Spots (Cuisine)</option>
<option value="Shopping">Shopping (Markets)</option>
</select>
<select id="priority-2" class="input-field w-full px-4 py-2 rounded-lg cursor-pointer">
<option value="Food Spots">Food Spots (Cuisine)</option>
<option value="Tourist Spots">Tourist Spots (Scenic)</option>
<option value="Shopping">Shopping (Markets)</option>
</select>
<select id="priority-3" class="input-field w-full px-4 py-2 rounded-lg cursor-pointer">
<option value="Shopping">Shopping (Markets)</option>
<option value="Tourist Spots">Tourist Spots (Scenic)</option>
<option value="Food Spots">Food Spots (Cuisine)</option>
</select>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Priority dropdowns allow duplicate selections.

All three <select> elements share the same options and can independently be set to the same value (e.g., "Food Spots" for all three priorities). This would produce a broken itinerary with only one category. Add client-side validation or dynamically filter chosen values from subsequent dropdowns.

🤖 Prompt for AI Agents
In `@index.html` around lines 97 - 111, The three select elements (priority-1,
priority-2, priority-3) currently allow duplicate categories; implement
client-side logic to enforce unique selections by listening for change events on
these IDs and either (a) disable the selected option value in the other two
selects or (b) rebuild their option lists to exclude already-chosen values, and
also validate on form submission to prevent duplicates; update the event
handlers to re-enable options when a selection is changed and ensure initial
state handles defaults.


Quick start

1. Copy `.env.example` to `.env` and fill values (especially `JWT_SECRET`).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if .env.example exists anywhere in the repo
fd -H ".env.example"

Repository: tinkerhub/tink-her-hack-temp

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Also check the README.md content around line 7 to confirm the reference
head -n 10 README.md

Repository: tinkerhub/tink-her-hack-temp

Length of output: 271


Add the missing .env.example file.

The README instructs users to copy .env.example to .env (line 7), but this file is not included in the repository. Without it, developers cannot proceed with setup and won't know which environment variables are required (e.g., JWT_SECRET, PORT, DB_PATH).

🤖 Prompt for AI Agents
In `@README.md` at line 7, The repository is missing the referenced .env.example
file; add a new .env.example file to the repo that lists all required
environment variables (at minimum JWT_SECRET, PORT, DB_PATH) with
placeholder/example values and brief comments, so developers can copy it to .env
as instructed in README; ensure the filename matches exactly ".env.example" and
include any other env keys used by startup code (search for usages of
process.env.JWT_SECRET, process.env.PORT, process.env.DB_PATH, etc.) to document
them in the example file.


### The Solution
[How are you solving it?]
we created a website that assits the passenger in all aspects- to plan, to know the destinations and while travelling they could know the necessary spots available near them.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo: "assits" → "assists".

🧰 Tools
🪛 LanguageTool

[grammar] ~64-~64: Ensure spelling is correct
Context: ... The Solution we created a website that assits the passenger in all aspects- to plan, ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
In `@README.md` at line 64, Typo: the README sentence contains "assits" instead of
"assists"; update the sentence fragment "we created a website that assits the
passenger..." to use the correct spelling "assists" so it reads "we created a
website that assists the passenger..." (keep the rest of the sentence
unchanged).

Comment on lines +25 to +46
async function apiRequest(path, method = 'GET', body) {
const opts = { method, headers: { 'Content-Type': 'application/json' } };
if (authToken) opts.headers['Authorization'] = `Bearer ${authToken}`;
if (body) opts.body = JSON.stringify(body);

console.log(`API: ${method} ${path}`, body ? body : '');

try {
const res = await fetch(API_BASE + path, opts);
const text = await res.text();
let data;
try { data = text ? JSON.parse(text) : null; } catch (e) { data = text; }

console.log(`API Response: ${res.status}`, data);

if (!res.ok) throw { status: res.status, body: data };
return data;
} catch (err) {
console.error(`API Error on ${path}:`, err);
throw err;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

apiRequest does not handle network failures distinctly from HTTP errors.

When fetch itself throws (e.g., network offline, DNS failure), the catch block re-throws an error without status or body properties. Callers that inspect err.status (line 70) will see undefined, potentially triggering unintended auto-registration. Consider normalizing the error shape.

🤖 Prompt for AI Agents
In `@script9.js` around lines 25 - 46, The apiRequest function currently re-throws
any exception from fetch/raw network failures without a consistent shape, so
callers that read err.status/err.body get undefined; modify apiRequest to
normalize all errors into a consistent object (e.g., { status: 0, body: <error
message or original error> } for network/fetch exceptions) and still preserve
HTTP error objects thrown when res.ok is false; specifically update the catch
block in apiRequest (which wraps fetch/API_BASE) to detect when no Response was
received and throw a normalized error with status and body properties (and
include the original error under a field like cause) so callers that check
err.status/err.body behave deterministically.

Comment on lines +67 to +97
} catch (err) {
console.log('Initial login failed:', err.status);
// Only auto-register on 401 (user not found)
if (err.status === 401) {
try {
console.log('User not found, attempting registration...');
const regRes = await apiRequest('/auth/register', 'POST', { username, password });
console.log('Registration response:', regRes);

if (regRes && regRes.data) {
console.log('Registration successful, logging in...');
const loginRes = await apiRequest('/auth/login', 'POST', { username, password });
if (loginRes && loginRes.data && loginRes.data.token) {
authToken = loginRes.data.token;
localStorage.setItem('sanchaari_token', authToken);
currentUser = username;
document.getElementById('display-username').innerText = currentUser;
showSection('mode-selection');
return;
}
}
} catch (regErr) {
console.error('Registration/Login error:', regErr);
alert('Auth failed: ' + (regErr.body?.error?.message || 'Unknown error'));
return;
}
} else {
alert('Login failed: ' + (err.body?.error?.message || err.status));
return;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Auto-register on 401 misfires when the password is wrong for an existing user.

The backend returns 401 for both "user not found" and "wrong password". This code interprets any 401 as "user not found" and attempts registration, which then fails with a 409 ("Username already exists"), producing a confusing error. Either differentiate the server's 401 responses or remove the auto-register behavior and provide explicit register/login flows.

🤖 Prompt for AI Agents
In `@script9.js` around lines 67 - 97, The code currently treats any 401 in the
initial login flow as "user not found" and auto-attempts registration; change
this to avoid misfires by either (A) checking the server error payload
(err.body?.error?.code or err.body?.error?.message) and only invoking the
registration branch when the error explicitly indicates user-not-found, or (B)
removing the automatic registration branch entirely and instead surface a clear
UI pathway to call apiRequest('/auth/register', 'POST', ...) from an explicit
registration action; update the catch block around the initial login (the
try/catch that references apiRequest, authToken, localStorage, currentUser,
document.getElementById('display-username'), and showSection) so it no longer
attempts registration on every 401 unless the error body proves the user is
missing.

if (error) return res.status(400).json({ error: { message: error.message } });

const { username, password } = value;
const hashed = bcrypt.hashSync(password, 10);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

bcrypt.hashSync blocks the event loop.

With 10 salt rounds, hashing takes ~100ms of synchronous CPU time, stalling all concurrent requests. Use the async variant instead.

⚡ Suggested fix
-    const hashed = bcrypt.hashSync(password, 10);
+    const hashed = await bcrypt.hash(password, 10);
🤖 Prompt for AI Agents
In `@src/routes/auth.js` at line 19, Replace the blocking bcrypt.hashSync call
with the asynchronous bcrypt.hash in the auth route: make the route handler
async (or return a Promise), call await bcrypt.hash(password, 10) instead of
bcrypt.hashSync(password, 10), and handle errors with try/catch so failures to
hash are caught and logged/responded to rather than crashing the request; update
any references to the synchronous "hashed" assignment accordingly (e.g., where
"hashed" is used to create the user or compare later).

const row = await db.get('SELECT id, username, password FROM users WHERE username = ?', username);
if (!row) return res.status(401).json({ error: { message: 'Invalid credentials' } });

const ok = bcrypt.compareSync(password, row.password);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

bcrypt.compareSync also blocks the event loop — same concern as hashSync.

⚡ Suggested fix
-    const ok = bcrypt.compareSync(password, row.password);
+    const ok = await bcrypt.compare(password, row.password);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const ok = bcrypt.compareSync(password, row.password);
const ok = await bcrypt.compare(password, row.password);
🤖 Prompt for AI Agents
In `@src/routes/auth.js` at line 43, The code uses the blocking bcrypt.compareSync
call (const ok = bcrypt.compareSync(password, row.password)); replace it with
the non-blocking async bcrypt.compare: make the enclosing handler function async
(or return a Promise), await bcrypt.compare(password, row.password), and handle
errors; update the variable 'ok' to receive the awaited result and proceed with
the same success/failure flow (including early returns) so the event loop is not
blocked.

Comment on lines +8 to +12
const createSchema = Joi.object({
destination: Joi.string().required(),
days: Joi.number().integer().min(1).max(30).required(),
priorities: Joi.array().items(Joi.string()).length(3).required()
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Priorities are not validated against known activity categories.

The priorities array accepts any 3 strings, but generateItineraryItems uses them as keys to look up city data (e.g., "Food Spots", "Tourist Spots", "Shopping"). If a user submits arbitrary strings, every activity falls back to the generic "${activityType} near ${destination}" placeholder. Consider adding .valid() to constrain allowed values:

Proposed fix
+const VALID_PRIORITIES = ['Food Spots', 'Tourist Spots', 'Shopping'];
+
 const createSchema = Joi.object({
   destination: Joi.string().required(),
   days: Joi.number().integer().min(1).max(30).required(),
-  priorities: Joi.array().items(Joi.string()).length(3).required()
+  priorities: Joi.array().items(Joi.string().valid(...VALID_PRIORITIES)).length(3).required()
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const createSchema = Joi.object({
destination: Joi.string().required(),
days: Joi.number().integer().min(1).max(30).required(),
priorities: Joi.array().items(Joi.string()).length(3).required()
});
const VALID_PRIORITIES = ['Food Spots', 'Tourist Spots', 'Shopping'];
const createSchema = Joi.object({
destination: Joi.string().required(),
days: Joi.number().integer().min(1).max(30).required(),
priorities: Joi.array().items(Joi.string().valid(...VALID_PRIORITIES)).length(3).required()
});
🤖 Prompt for AI Agents
In `@src/routes/itineraries.js` around lines 8 - 12, The priorities array in
createSchema currently allows any three strings, causing generateItineraryItems
to receive invalid keys and produce generic placeholders; update createSchema's
priorities validation to restrict values to the known activity categories used
by generateItineraryItems (e.g., "Food Spots", "Tourist Spots", "Shopping" — or
whatever exact category strings generateItineraryItems expects) by replacing
Joi.string() with Joi.string().valid(...) inside the priorities array schema
while keeping .length(3). Ensure the valid list matches the keys used in
generateItineraryItems so invalid submissions are rejected.

Comment on lines +13 to +16
app.listen(port, () => {
logger.info(`Server listening on port ${port}`);
console.log(`Server listening on port ${port}`);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Port-in-use errors won't be caught by the try/catch.

app.listen() is callback-based; bind errors (e.g., EADDRINUSE) are emitted as 'error' events on the returned server object, not thrown into the surrounding try/catch. Consider capturing the server handle and attaching an error listener.

🛠️ Suggested fix
-    app.listen(port, () => {
+    const server = app.listen(port, () => {
       logger.info(`Server listening on port ${port}`);
       console.log(`Server listening on port ${port}`);
     });
+    server.on('error', (err) => {
+      logger.error('Server error', err);
+      console.error(err);
+      process.exit(1);
+    });
🤖 Prompt for AI Agents
In `@src/server.js` around lines 13 - 16, app.listen's bind errors are emitted on
the returned server object, not thrown into the surrounding try/catch; change
the call to capture the server handle (e.g., const server = app.listen(port, ()
=> { logger.info(...); })) and attach an error listener on that handle
(server.on('error', err => { logger.error('Server failed to start', err); handle
specific cases like err.code === 'EADDRINUSE' and exit or retry as appropriate
})), ensuring you log the real error details rather than relying only on the
callback.

Comment on lines +107 to +117
.page-section {
display: none;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.page-section.active {
display: flex; /* or block depending on layout */
opacity: 1;
transform: translateY(0);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Page transition animation won't work due to display: nonedisplay: flex toggle.

CSS transitions on opacity and transform do not fire when display changes from none to flex, because the element is not in the layout to begin with. The page will just snap into view without the intended fade/slide.

A common fix is to separate the display toggle from the animation trigger — e.g., set display: flex first, then add the active class on the next frame (via requestAnimationFrame or a minimal setTimeout in JS), or use visibility/pointer-events instead of display: none.

🤖 Prompt for AI Agents
In `@style9.css` around lines 107 - 117, The .page-section transition won't run
because switching from display: none to display: flex prevents CSS transitions;
update the approach so the element stays in layout and only
visibility/opacity/transform change: keep .page-section as display: flex (or
block) always, change the inactive state to visibility: hidden; pointer-events:
none; opacity: 0; transform: translateY(20px); and have .page-section.active set
visibility: visible; pointer-events: auto; opacity: 1; transform: translateY(0)
so the opacity/transform transitions run; alternatively, if you must toggle
display, modify the JavaScript that adds .page-section.active (or sets display)
to first set display: flex, then add .active on the next animation frame (using
requestAnimationFrame or a minimal setTimeout) so the transition can start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant