Latest commit

History

194 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🔍 Lost & Found - Reuniting People with Lost Items & Pets

Lost & Found Banner

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching. The platform combines real-time mapping, advanced search algorithms, and secure user authentication to create a comprehensive lost-and-found ecosystem.

Table of Contents

Project Overview

This application addresses the challenge of efficiently connecting people who have lost items with those who have found them. By leveraging geospatial indexing and caching strategies, the platform delivers fast, location-aware search results while maintaining data security and system reliability.

Key Technical Highlights:

  • Geospatial queries with MongoDB 2dsphere indexes
  • Redis-backed rate limiting and caching
  • JWT-based authentication with refresh token rotation
  • Automated image optimization via Cloudinary CDN
  • XSS protection through input sanitization with express-mongo-sanitize
  • Comprehensive input validation using Zod schemas

Preview

Post PageCreate Post
DashboardHomepage

Core Features

Geospatial Posting & Discovery

Users can create posts with precise coordinates using Leaflet.js integration and OpenStreetMap's Nominatim API. The system implements MongoDB geospatial indexes for efficient radius-based queries, enabling users to discover nearby lost/found items within customizable distance ranges.

Advanced Search & Filtering

Multi-parameter search functionality includes text matching, date ranges, categories, and location-based filtering. Search queries are optimized through Redis caching with intelligent TTL management (1-hour expiration), reducing external API calls and improving response times.

Printable Flyer Generator

Automated generation of professional PDF flyers with QR codes linking back to the online post. Templates are optimized for A4 printing and include customizable layouts that adapt to different item types.

User Management System

Secure authentication flow with JWT tokens (access + refresh), email verification, and password recovery. User profiles maintain posting history with dashboard analytics for tracking active and resolved posts. Users can bookmark posts for later reference and manage their saved collections.

Comment System

Real-time commenting functionality allows users to ask questions, provide updates, or coordinate meetups directly on posts. Comments are rate-limited to prevent spam and support threaded discussions.

Technical Architecture

Technology Stack

Next.jsTypeScriptExpress.jsReactMongoDBRedisCloudinaryLeafletSASSContext-API

Frontend:

  • Next.js 14 with App Router
  • TypeScript for type safety
  • SCSS Modules for component-scoped styling
  • Leaflet.js for interactive maps
  • React Context API for state management

Backend:

  • Express.js with TypeScript
  • Mongoose ODM for MongoDB interactions
  • Redis for session storage and caching
  • Helmet.js for security headers
  • Morgan for request logging
  • express-mongo-sanitize for NoSQL injection prevention
  • Zod for schema validation

Infrastructure:

  • MongoDB Atlas for database hosting
  • Redis Cloud for caching layer
  • Cloudinary for image CDN
  • Vercel for frontend deployment
  • Railway/Render for backend deployment

API Documentation

Base URL

Production: https://api.lostfound.ro/api/v1
Development: http://localhost:8000/api/v1

Authentication Routes (/auth)

MethodEndpointRate LimitDescription
POST/register5/10minCreate new user account with email verification
POST/login10/5minAuthenticate user and issue JWT tokens
POST/logout-Invalidate refresh token and clear cookies
POST/refresh-token-Generate new access token using refresh token
POST/verify-email10/minConfirm email address with verification code
POST/forgot-password10/minRequest password reset email
POST/reset-password10/minReset password using token from email

Authentication Flow:

  1. User registers → Email verification sent
  2. User verifies email → Account activated
  3. User logs in → Access token (15min) + Refresh token (7d) issued
  4. Access token expires → Client requests new token using refresh token
  5. Refresh token expires → User must log in again

Post Management Routes (/post)

MethodEndpointAuthRate LimitDescription
POST/create93/10minCreate new lost/found post with images
GET/:postId-30/minRetrieve single post by ID
PUT/edit/:postId20/5minUpdate post details and images
PATCH/solve/:postId30/minMark post as resolved
DELETE/delete/:postId10/5minDelete user's own post
GET/user-posts30/minGet all posts by authenticated user
GET/latest-30/minFetch recent posts with pagination

Post Creation Example:

POST/api/v1/post/createContent-Type: multipart/form-data
Authorization: Bearer{access_token}{title: "Lost Black Labrador",description: "Last seen near Central Park",category: "pet",type: "lost",location: {lat: 44.4268,lon: 26.1025,display_name: "Bucharest, Romania"},contactInfo: {phone: "+40123456789",email: "contact@example.com"},images: [File,File]// Max 5 images, 5MB each}

User Management Routes (/user)

MethodEndpointAuthRate LimitDescription
GET/profile30/minGet authenticated user's profile
GET/public-profile/:id-30/minView public user profile
PUT/change-password2/minUpdate user password
PUT/change-profile-image2/minUpload new profile picture
DELETE/delete-account2/minPermanently delete user account
GET/saved-posts-Retrieve user's bookmarked posts
POST/save-post30/minBookmark a post
POST/remove-post30/minRemove post from bookmarks

Geocoding Routes (/geo)

MethodEndpointRate LimitDescription
GET/search?q={query}&limit={n}60/minForward geocoding (address → coordinates)
GET/reverse?lat={lat}&lon={lon}60/minReverse geocoding (coordinates → address)
GET/health-Service health check

Geocoding Features:

  • Results cached in Redis for 1 hour
  • Country-specific to Romania (countrycodes=ro)
  • Coordinate validation: lat ∈ [43.5, 48.3], lon ∈ [20.2, 29.7]
  • Automatic language localization (Romanian)
  • Deduplicated results with importance scoring

Comment Routes (/comment)

MethodEndpointAuthRate LimitDescription
POST/create5/minAdd comment to post
DELETE/delete/:commentId5/minDelete own comment

Search Routes (/search)

MethodEndpointDescription
GET/posts?q={query}&category={cat}&location={loc}&radius={km}&dateFrom={date}&dateTo={date}Advanced post search

Search Parameters:

  • q: Text search in title/description
  • category: Filter by category (pet, electronics, documents, etc.)
  • location: Center point for radius search
  • radius: Search radius in kilometers
  • dateFrom/dateTo: Filter by posting date range

Security Implementation

Input Validation & Sanitization

Zod Schema Validation - All incoming requests are validated against TypeScript-first schemas before reaching controllers. This ensures type safety and catches malformed data early in the request lifecycle.

// Example: Post creation schemaconstcreatePostSchema=z.object({title: z.string().min(3).max(100),description: z.string().min(10).max(2000),category: z.enum(['pet','electronics','documents','jewelry','other']),type: z.enum(['lost','found']),location: z.object({lat: z.number().min(43.5).max(48.3),lon: z.number().min(20.2).max(29.7),display_name: z.string()})});

NoSQL Injection Prevention - express-mongo-sanitize middleware strips out $ and . characters from user input, preventing MongoDB operator injection attacks. This protects against malicious queries that attempt to manipulate database operations.

// Sanitization applied globally to all routesapp.use(mongoSanitize());// Example attack prevented:// { "email": { "$gt": "" }} → { "email": "" }

Rate Limiting Architecture

Redis-backed rate limiting prevents abuse and ensures fair resource allocation. Different endpoints have tiered limits based on their resource intensity:

Endpoint TypeWindowLimitRationale
Registration10 min5Prevent bot account creation
Login5 min10Balance security vs. user experience
Post Creation10 min93Allow legitimate use while preventing spam
Image Upload5 min115Protect storage and bandwidth
Geocoding1 min60Respect external API fair use
Comments1 min5Prevent spam without hindering discussion
Profile Updates1 min2Critical operations need strict limits

Rate limit state is stored in Redis with key prefixes (rl_register:, rl_login:, etc.) for namespace isolation. The system returns standardized error responses with retry-after headers compliant with RFC 6585.

Authentication & Authorization

JWT Token Strategy:

  • Access Tokens: Short-lived (15 minutes), contain user ID and role
  • Refresh Tokens: Long-lived (7 days), stored in httpOnly cookies
  • Token Rotation: Each refresh generates new token pair, old tokens invalidated
  • Signature Algorithm: HS256 with secrets ≥32 characters

Cookie Security:

res.cookie('refreshToken',token,{httpOnly: true,// Prevent XSS accesssecure: true,// HTTPS only in productionsameSite: 'strict',// CSRF protectionmaxAge: 7*24*60*60*1000// 7 days});

Password Security:

  • bcrypt hashing with salt rounds = 12
  • Minimum 8 characters with complexity requirements
  • Passwords never logged or returned in responses
  • Secure password reset with time-limited tokens

HTTP Security Headers (Helmet.js)

app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],imgSrc: ["'self'","data:","https://res.cloudinary.com"],scriptSrc: ["'self'","'unsafe-inline'"],// Next.js requirement}},hsts: {maxAge: 31536000,includeSubDomains: true,preload: true}}));

Enabled protections include CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

File Upload Security

Multer Configuration:

  • Memory storage (no disk writes in development)
  • MIME type validation before processing
  • Size limits: 5MB per file, max 5 files per request
  • Allowed formats: JPEG, JPG, PNG, WebP only
  • Error handling for malformed uploads

Cloudinary Integration:

  • Automatic format optimization (WebP conversion)
  • Lazy transformation for responsive images
  • Signed upload URLs prevent unauthorized uploads
  • CDN delivery reduces origin server load

CORS Policy

app.use(cors({origin: process.env.FRONTEND_URL,// Whitelist specific origincredentials: true,// Allow cookiesmethods: ['GET','POST','PUT','PATCH','DELETE'],allowedHeaders: ['Content-Type','Authorization']}));

Strict CORS configuration prevents cross-origin attacks while enabling authenticated requests from the frontend.

Performance Optimizations

Caching Strategy

Redis Caching Layer:

  • Geocoding responses: 1-hour TTL (key: search:{query}:{limit})
  • Reverse geocoding: 1-hour TTL (key: reverse:{lat}:{lon})
  • Rate limit counters: Sliding window with automatic expiration
  • Session tokens: TTL matches JWT expiration

Cache hit rate monitoring shows ~75% cache hits for geocoding queries, reducing external API calls and improving response times from ~800ms to ~15ms.

Database Indexing

MongoDB Indexes:

// Geospatial index for location queriespostSchema.index({location: '2dsphere'});// Compound index for filtered searchespostSchema.index({category: 1,type: 1,createdAt: -1});// Text index for full-text searchpostSchema.index({title: 'text',description: 'text'});// User lookup optimizationpostSchema.index({userId: 1,status: 1});

Query performance benchmarks show 95th percentile latency under 50ms for indexed queries vs. 2000ms+ for full collection scans.

Image Optimization Pipeline

Cloudinary Transformations:

  • Automatic WebP conversion with fallback to original format
  • Responsive image variants (thumbnail, medium, full)
  • Lazy loading with low-quality image placeholders (LQIP)
  • CDN edge caching for global delivery

Optimization Results:

  • Average image size: 2.3MB → 180KB (WebP)
  • Page load time: 4.2s → 1.8s
  • Bandwidth savings: ~92%

Frontend Optimizations

Next.js Features:

  • Automatic code splitting per route
  • Server-side rendering for SEO and initial load performance
  • Static generation for public pages
  • Image component with built-in lazy loading
  • Font optimization with Geist preloading

Bundle Analysis:

  • Initial JS bundle: 142KB gzipped
  • First Contentful Paint: ~1.2s
  • Time to Interactive: ~2.3s
  • Lighthouse Performance Score: 94/100

Technical Challenges

Geospatial Accuracy & Validation

Challenge: Ensuring coordinates are valid and fall within Romania's boundaries while handling edge cases like users near borders or coordinates from external sources.

Solution: Implemented strict Zod validation with min/max constraints on latitude (43.5-48.3°N) and longitude (20.2-29.7°E). Added fallback mechanisms when Nominatim API fails—system gracefully degrades to displaying raw coordinates rather than throwing errors.

constreverseSchema=z.object({lat: z.coerce.number().min(43.5).max(48.3),lon: z.coerce.number().min(20.2).max(29.7)});// Fallback response on API failurecatch(error){res.json({display_name: `${lat.toFixed(5)}, ${lon.toFixed(5)}`,address: {},
lat, lon
});}

Concurrent Update Conflicts

Challenge: Race conditions when multiple users interact with the same post simultaneously (editing, commenting, marking resolved).

Solution: Leveraged MongoDB's atomic update operators ($set, $push, $inc) and implemented optimistic locking with version fields. Critical operations use transactions to ensure data consistency.

// Atomic operation prevents race conditionsawaitPost.findByIdAndUpdate(postId,{$set: {status: 'solved',solvedAt: newDate()}},{new: true,runValidators: true});

External API Resilience

Challenge: Nominatim API rate limits (1 request/second) and occasional timeouts causing user-facing errors.

Solution: Three-layered approach:

  1. Redis caching with 1-hour TTL reduces API calls by ~75%
  2. Timeout configuration (5s) prevents hanging requests
  3. Graceful degradation returns partial data instead of failing

Rate limiting on the geocoding endpoint (60/min) ensures compliance with Nominatim's usage policy while accommodating legitimate user activity.

Scalability & Resource Management

Challenge: As user base grows, managing database connections, Redis connections, and memory usage becomes critical.

Solution:

  • MongoDB connection pooling (min: 10, max: 50 connections)
  • Redis connection reuse with single client instance
  • Image uploads limited to 5MB to prevent memory exhaustion
  • Rate limiting prevents resource starvation from malicious actors
  • Horizontal scaling strategy with load balancer-ready stateless design

Search Performance at Scale

Challenge: Text search across thousands of posts with multiple filters (location, category, date) must remain fast.

Solution: Implemented compound indexes covering common query patterns and MongoDB aggregation pipeline for complex searches. Future optimization plan includes Elasticsearch integration for full-text search once post volume exceeds 100K records.

Mobile Responsive Design

Mobile HomepageMobile PostMobile Map

Fully responsive design with touch-optimized map controls, collapsible filters, and mobile-first form layouts. CSS Grid and Flexbox ensure consistent layouts across devices. Breakpoints at 768px and 1024px accommodate tablets and desktops.

Installation

Prerequisites

  • Node.js 18+ and npm
  • MongoDB 5.0+
  • Redis 6.0+
  • Cloudinary account (free tier sufficient)

Setup Instructions

# Clone repository
git clone https://github.com/Rotis-Web/lostfound.git
cd lostfound
# Install frontend dependenciescd client
npm install
# Install backend dependenciescd ../server
npm install
# Start MongoDB and Redis (if running locally)# macOS with Homebrew:
brew services start mongodb-community
brew services start redis
# Run development servers
npm run dev:all
# This starts both frontend (port 3000) and backend (port 8000)

Environment Configuration

Frontend Configuration

Create client/.env.local:

NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name

Backend Configuration

Create server/.env:

# Server
PORT=8000
NODE_ENV=development
# Database
MONGO_URI=mongodb://localhost:27017/lostfound
# Production: mongodb+srv://username:password@cluster.mongodb.net/lostfound# Redis
REDIS_URL=redis://localhost:6379
# Production: redis://username:password@host:port# Application URLs
APP_ORIGIN=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# JWT Configuration (generate random 32+ char strings)
JWT_SECRET=your_secure_secret_min_32_chars_use_openssl_rand
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret_different_from_above
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary (sign up at cloudinary.com)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_specific_password

Generating Secure Secrets:

# Use OpenSSL to generate random secrets
openssl rand -base64 32

Design Philosophy

The interface prioritizes clarity and accessibility with a warm color palette that conveys hope and urgency. The design system balances vibrant accent colors with professional neutrals to create an approachable yet trustworthy aesthetic.

Color Palette

ColorHexUsage
Yellow Primary#ffd700CTAs, highlights - conveys energy and optimism
Dark Blue#2c3e60Headers, text - inspires trust and professionalism
Orange Accent#f57a4eImportant buttons, alerts - draws attention
Green Success#51e188Success messages, resolved posts
Red Alert#ff4444Error states, urgent actions
Neutral Gray#9ca3afSecondary text, borders, disabled states

Typography

  • Font Family: Geist Sans - Modern, highly legible sans-serif optimized for UI
  • Heading Scale: 2.5rem / 2rem / 1.5rem / 1.25rem / 1rem
  • Body Text: 1rem (16px) with 1.5 line height for optimal readability
  • Code/Monospace: Geist Mono for technical content

Built to reunite people with what matters most

TypeScriptNext.jsExpressMongoDBRedis

License: MIT | Developer: Alexandru Rotar

About

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

194 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🔍 Lost & Found - Reuniting People with Lost Items & Pets

Lost & Found Banner

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching. The platform combines real-time mapping, advanced search algorithms, and secure user authentication to create a comprehensive lost-and-found ecosystem.

Table of Contents

Project Overview

This application addresses the challenge of efficiently connecting people who have lost items with those who have found them. By leveraging geospatial indexing and caching strategies, the platform delivers fast, location-aware search results while maintaining data security and system reliability.

Key Technical Highlights:

  • Geospatial queries with MongoDB 2dsphere indexes
  • Redis-backed rate limiting and caching
  • JWT-based authentication with refresh token rotation
  • Automated image optimization via Cloudinary CDN
  • XSS protection through input sanitization with express-mongo-sanitize
  • Comprehensive input validation using Zod schemas

Preview

Post PageCreate Post
DashboardHomepage

Core Features

Geospatial Posting & Discovery

Users can create posts with precise coordinates using Leaflet.js integration and OpenStreetMap's Nominatim API. The system implements MongoDB geospatial indexes for efficient radius-based queries, enabling users to discover nearby lost/found items within customizable distance ranges.

Advanced Search & Filtering

Multi-parameter search functionality includes text matching, date ranges, categories, and location-based filtering. Search queries are optimized through Redis caching with intelligent TTL management (1-hour expiration), reducing external API calls and improving response times.

Printable Flyer Generator

Automated generation of professional PDF flyers with QR codes linking back to the online post. Templates are optimized for A4 printing and include customizable layouts that adapt to different item types.

User Management System

Secure authentication flow with JWT tokens (access + refresh), email verification, and password recovery. User profiles maintain posting history with dashboard analytics for tracking active and resolved posts. Users can bookmark posts for later reference and manage their saved collections.

Comment System

Real-time commenting functionality allows users to ask questions, provide updates, or coordinate meetups directly on posts. Comments are rate-limited to prevent spam and support threaded discussions.

Technical Architecture

Technology Stack

Next.jsTypeScriptExpress.jsReactMongoDBRedisCloudinaryLeafletSASSContext-API

Frontend:

  • Next.js 14 with App Router
  • TypeScript for type safety
  • SCSS Modules for component-scoped styling
  • Leaflet.js for interactive maps
  • React Context API for state management

Backend:

  • Express.js with TypeScript
  • Mongoose ODM for MongoDB interactions
  • Redis for session storage and caching
  • Helmet.js for security headers
  • Morgan for request logging
  • express-mongo-sanitize for NoSQL injection prevention
  • Zod for schema validation

Infrastructure:

  • MongoDB Atlas for database hosting
  • Redis Cloud for caching layer
  • Cloudinary for image CDN
  • Vercel for frontend deployment
  • Railway/Render for backend deployment

API Documentation

Base URL

Production: https://api.lostfound.ro/api/v1
Development: http://localhost:8000/api/v1

Authentication Routes (/auth)

MethodEndpointRate LimitDescription
POST/register5/10minCreate new user account with email verification
POST/login10/5minAuthenticate user and issue JWT tokens
POST/logout-Invalidate refresh token and clear cookies
POST/refresh-token-Generate new access token using refresh token
POST/verify-email10/minConfirm email address with verification code
POST/forgot-password10/minRequest password reset email
POST/reset-password10/minReset password using token from email

Authentication Flow:

  1. User registers → Email verification sent
  2. User verifies email → Account activated
  3. User logs in → Access token (15min) + Refresh token (7d) issued
  4. Access token expires → Client requests new token using refresh token
  5. Refresh token expires → User must log in again

Post Management Routes (/post)

MethodEndpointAuthRate LimitDescription
POST/create93/10minCreate new lost/found post with images
GET/:postId-30/minRetrieve single post by ID
PUT/edit/:postId20/5minUpdate post details and images
PATCH/solve/:postId30/minMark post as resolved
DELETE/delete/:postId10/5minDelete user's own post
GET/user-posts30/minGet all posts by authenticated user
GET/latest-30/minFetch recent posts with pagination

Post Creation Example:

POST/api/v1/post/createContent-Type: multipart/form-data
Authorization: Bearer{access_token}{title: "Lost Black Labrador",description: "Last seen near Central Park",category: "pet",type: "lost",location: {lat: 44.4268,lon: 26.1025,display_name: "Bucharest, Romania"},contactInfo: {phone: "+40123456789",email: "contact@example.com"},images: [File,File]// Max 5 images, 5MB each}

User Management Routes (/user)

MethodEndpointAuthRate LimitDescription
GET/profile30/minGet authenticated user's profile
GET/public-profile/:id-30/minView public user profile
PUT/change-password2/minUpdate user password
PUT/change-profile-image2/minUpload new profile picture
DELETE/delete-account2/minPermanently delete user account
GET/saved-posts-Retrieve user's bookmarked posts
POST/save-post30/minBookmark a post
POST/remove-post30/minRemove post from bookmarks

Geocoding Routes (/geo)

MethodEndpointRate LimitDescription
GET/search?q={query}&limit={n}60/minForward geocoding (address → coordinates)
GET/reverse?lat={lat}&lon={lon}60/minReverse geocoding (coordinates → address)
GET/health-Service health check

Geocoding Features:

  • Results cached in Redis for 1 hour
  • Country-specific to Romania (countrycodes=ro)
  • Coordinate validation: lat ∈ [43.5, 48.3], lon ∈ [20.2, 29.7]
  • Automatic language localization (Romanian)
  • Deduplicated results with importance scoring

Comment Routes (/comment)

MethodEndpointAuthRate LimitDescription
POST/create5/minAdd comment to post
DELETE/delete/:commentId5/minDelete own comment

Search Routes (/search)

MethodEndpointDescription
GET/posts?q={query}&category={cat}&location={loc}&radius={km}&dateFrom={date}&dateTo={date}Advanced post search

Search Parameters:

  • q: Text search in title/description
  • category: Filter by category (pet, electronics, documents, etc.)
  • location: Center point for radius search
  • radius: Search radius in kilometers
  • dateFrom/dateTo: Filter by posting date range

Security Implementation

Input Validation & Sanitization

Zod Schema Validation - All incoming requests are validated against TypeScript-first schemas before reaching controllers. This ensures type safety and catches malformed data early in the request lifecycle.

// Example: Post creation schemaconstcreatePostSchema=z.object({title: z.string().min(3).max(100),description: z.string().min(10).max(2000),category: z.enum(['pet','electronics','documents','jewelry','other']),type: z.enum(['lost','found']),location: z.object({lat: z.number().min(43.5).max(48.3),lon: z.number().min(20.2).max(29.7),display_name: z.string()})});

NoSQL Injection Prevention - express-mongo-sanitize middleware strips out $ and . characters from user input, preventing MongoDB operator injection attacks. This protects against malicious queries that attempt to manipulate database operations.

// Sanitization applied globally to all routesapp.use(mongoSanitize());// Example attack prevented:// { "email": { "$gt": "" }} → { "email": "" }

Rate Limiting Architecture

Redis-backed rate limiting prevents abuse and ensures fair resource allocation. Different endpoints have tiered limits based on their resource intensity:

Endpoint TypeWindowLimitRationale
Registration10 min5Prevent bot account creation
Login5 min10Balance security vs. user experience
Post Creation10 min93Allow legitimate use while preventing spam
Image Upload5 min115Protect storage and bandwidth
Geocoding1 min60Respect external API fair use
Comments1 min5Prevent spam without hindering discussion
Profile Updates1 min2Critical operations need strict limits

Rate limit state is stored in Redis with key prefixes (rl_register:, rl_login:, etc.) for namespace isolation. The system returns standardized error responses with retry-after headers compliant with RFC 6585.

Authentication & Authorization

JWT Token Strategy:

  • Access Tokens: Short-lived (15 minutes), contain user ID and role
  • Refresh Tokens: Long-lived (7 days), stored in httpOnly cookies
  • Token Rotation: Each refresh generates new token pair, old tokens invalidated
  • Signature Algorithm: HS256 with secrets ≥32 characters

Cookie Security:

res.cookie('refreshToken',token,{httpOnly: true,// Prevent XSS accesssecure: true,// HTTPS only in productionsameSite: 'strict',// CSRF protectionmaxAge: 7*24*60*60*1000// 7 days});

Password Security:

  • bcrypt hashing with salt rounds = 12
  • Minimum 8 characters with complexity requirements
  • Passwords never logged or returned in responses
  • Secure password reset with time-limited tokens

HTTP Security Headers (Helmet.js)

app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],imgSrc: ["'self'","data:","https://res.cloudinary.com"],scriptSrc: ["'self'","'unsafe-inline'"],// Next.js requirement}},hsts: {maxAge: 31536000,includeSubDomains: true,preload: true}}));

Enabled protections include CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

File Upload Security

Multer Configuration:

  • Memory storage (no disk writes in development)
  • MIME type validation before processing
  • Size limits: 5MB per file, max 5 files per request
  • Allowed formats: JPEG, JPG, PNG, WebP only
  • Error handling for malformed uploads

Cloudinary Integration:

  • Automatic format optimization (WebP conversion)
  • Lazy transformation for responsive images
  • Signed upload URLs prevent unauthorized uploads
  • CDN delivery reduces origin server load

CORS Policy

app.use(cors({origin: process.env.FRONTEND_URL,// Whitelist specific origincredentials: true,// Allow cookiesmethods: ['GET','POST','PUT','PATCH','DELETE'],allowedHeaders: ['Content-Type','Authorization']}));

Strict CORS configuration prevents cross-origin attacks while enabling authenticated requests from the frontend.

Performance Optimizations

Caching Strategy

Redis Caching Layer:

  • Geocoding responses: 1-hour TTL (key: search:{query}:{limit})
  • Reverse geocoding: 1-hour TTL (key: reverse:{lat}:{lon})
  • Rate limit counters: Sliding window with automatic expiration
  • Session tokens: TTL matches JWT expiration

Cache hit rate monitoring shows ~75% cache hits for geocoding queries, reducing external API calls and improving response times from ~800ms to ~15ms.

Database Indexing

MongoDB Indexes:

// Geospatial index for location queriespostSchema.index({location: '2dsphere'});// Compound index for filtered searchespostSchema.index({category: 1,type: 1,createdAt: -1});// Text index for full-text searchpostSchema.index({title: 'text',description: 'text'});// User lookup optimizationpostSchema.index({userId: 1,status: 1});

Query performance benchmarks show 95th percentile latency under 50ms for indexed queries vs. 2000ms+ for full collection scans.

Image Optimization Pipeline

Cloudinary Transformations:

  • Automatic WebP conversion with fallback to original format
  • Responsive image variants (thumbnail, medium, full)
  • Lazy loading with low-quality image placeholders (LQIP)
  • CDN edge caching for global delivery

Optimization Results:

  • Average image size: 2.3MB → 180KB (WebP)
  • Page load time: 4.2s → 1.8s
  • Bandwidth savings: ~92%

Frontend Optimizations

Next.js Features:

  • Automatic code splitting per route
  • Server-side rendering for SEO and initial load performance
  • Static generation for public pages
  • Image component with built-in lazy loading
  • Font optimization with Geist preloading

Bundle Analysis:

  • Initial JS bundle: 142KB gzipped
  • First Contentful Paint: ~1.2s
  • Time to Interactive: ~2.3s
  • Lighthouse Performance Score: 94/100

Technical Challenges

Geospatial Accuracy & Validation

Challenge: Ensuring coordinates are valid and fall within Romania's boundaries while handling edge cases like users near borders or coordinates from external sources.

Solution: Implemented strict Zod validation with min/max constraints on latitude (43.5-48.3°N) and longitude (20.2-29.7°E). Added fallback mechanisms when Nominatim API fails—system gracefully degrades to displaying raw coordinates rather than throwing errors.

constreverseSchema=z.object({lat: z.coerce.number().min(43.5).max(48.3),lon: z.coerce.number().min(20.2).max(29.7)});// Fallback response on API failurecatch(error){res.json({display_name: `${lat.toFixed(5)}, ${lon.toFixed(5)}`,address: {},
lat, lon
});}

Concurrent Update Conflicts

Challenge: Race conditions when multiple users interact with the same post simultaneously (editing, commenting, marking resolved).

Solution: Leveraged MongoDB's atomic update operators ($set, $push, $inc) and implemented optimistic locking with version fields. Critical operations use transactions to ensure data consistency.

// Atomic operation prevents race conditionsawaitPost.findByIdAndUpdate(postId,{$set: {status: 'solved',solvedAt: newDate()}},{new: true,runValidators: true});

External API Resilience

Challenge: Nominatim API rate limits (1 request/second) and occasional timeouts causing user-facing errors.

Solution: Three-layered approach:

  1. Redis caching with 1-hour TTL reduces API calls by ~75%
  2. Timeout configuration (5s) prevents hanging requests
  3. Graceful degradation returns partial data instead of failing

Rate limiting on the geocoding endpoint (60/min) ensures compliance with Nominatim's usage policy while accommodating legitimate user activity.

Scalability & Resource Management

Challenge: As user base grows, managing database connections, Redis connections, and memory usage becomes critical.

Solution:

  • MongoDB connection pooling (min: 10, max: 50 connections)
  • Redis connection reuse with single client instance
  • Image uploads limited to 5MB to prevent memory exhaustion
  • Rate limiting prevents resource starvation from malicious actors
  • Horizontal scaling strategy with load balancer-ready stateless design

Search Performance at Scale

Challenge: Text search across thousands of posts with multiple filters (location, category, date) must remain fast.

Solution: Implemented compound indexes covering common query patterns and MongoDB aggregation pipeline for complex searches. Future optimization plan includes Elasticsearch integration for full-text search once post volume exceeds 100K records.

Mobile Responsive Design

Mobile HomepageMobile PostMobile Map

Fully responsive design with touch-optimized map controls, collapsible filters, and mobile-first form layouts. CSS Grid and Flexbox ensure consistent layouts across devices. Breakpoints at 768px and 1024px accommodate tablets and desktops.

Installation

Prerequisites

  • Node.js 18+ and npm
  • MongoDB 5.0+
  • Redis 6.0+
  • Cloudinary account (free tier sufficient)

Setup Instructions

# Clone repository
git clone https://github.com/Rotis-Web/lostfound.git
cd lostfound
# Install frontend dependenciescd client
npm install
# Install backend dependenciescd ../server
npm install
# Start MongoDB and Redis (if running locally)# macOS with Homebrew:
brew services start mongodb-community
brew services start redis
# Run development servers
npm run dev:all
# This starts both frontend (port 3000) and backend (port 8000)

Environment Configuration

Frontend Configuration

Create client/.env.local:

NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name

Backend Configuration

Create server/.env:

# Server
PORT=8000
NODE_ENV=development
# Database
MONGO_URI=mongodb://localhost:27017/lostfound
# Production: mongodb+srv://username:password@cluster.mongodb.net/lostfound# Redis
REDIS_URL=redis://localhost:6379
# Production: redis://username:password@host:port# Application URLs
APP_ORIGIN=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# JWT Configuration (generate random 32+ char strings)
JWT_SECRET=your_secure_secret_min_32_chars_use_openssl_rand
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret_different_from_above
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary (sign up at cloudinary.com)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_specific_password

Generating Secure Secrets:

# Use OpenSSL to generate random secrets
openssl rand -base64 32

Design Philosophy

The interface prioritizes clarity and accessibility with a warm color palette that conveys hope and urgency. The design system balances vibrant accent colors with professional neutrals to create an approachable yet trustworthy aesthetic.

Color Palette

ColorHexUsage
Yellow Primary#ffd700CTAs, highlights - conveys energy and optimism
Dark Blue#2c3e60Headers, text - inspires trust and professionalism
Orange Accent#f57a4eImportant buttons, alerts - draws attention
Green Success#51e188Success messages, resolved posts
Red Alert#ff4444Error states, urgent actions
Neutral Gray#9ca3afSecondary text, borders, disabled states

Typography

  • Font Family: Geist Sans - Modern, highly legible sans-serif optimized for UI
  • Heading Scale: 2.5rem / 2rem / 1.5rem / 1.25rem / 1rem
  • Body Text: 1rem (16px) with 1.5 line height for optimal readability
  • Code/Monospace: Geist Mono for technical content

Built to reunite people with what matters most

TypeScriptNext.jsExpressMongoDBRedis

License: MIT | Developer: Alexandru Rotar

About

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

194 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🔍 Lost & Found - Reuniting People with Lost Items & Pets

Lost & Found Banner

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching. The platform combines real-time mapping, advanced search algorithms, and secure user authentication to create a comprehensive lost-and-found ecosystem.

Table of Contents

Project Overview

This application addresses the challenge of efficiently connecting people who have lost items with those who have found them. By leveraging geospatial indexing and caching strategies, the platform delivers fast, location-aware search results while maintaining data security and system reliability.

Key Technical Highlights:

  • Geospatial queries with MongoDB 2dsphere indexes
  • Redis-backed rate limiting and caching
  • JWT-based authentication with refresh token rotation
  • Automated image optimization via Cloudinary CDN
  • XSS protection through input sanitization with express-mongo-sanitize
  • Comprehensive input validation using Zod schemas

Preview

Post PageCreate Post
DashboardHomepage

Core Features

Geospatial Posting & Discovery

Users can create posts with precise coordinates using Leaflet.js integration and OpenStreetMap's Nominatim API. The system implements MongoDB geospatial indexes for efficient radius-based queries, enabling users to discover nearby lost/found items within customizable distance ranges.

Advanced Search & Filtering

Multi-parameter search functionality includes text matching, date ranges, categories, and location-based filtering. Search queries are optimized through Redis caching with intelligent TTL management (1-hour expiration), reducing external API calls and improving response times.

Printable Flyer Generator

Automated generation of professional PDF flyers with QR codes linking back to the online post. Templates are optimized for A4 printing and include customizable layouts that adapt to different item types.

User Management System

Secure authentication flow with JWT tokens (access + refresh), email verification, and password recovery. User profiles maintain posting history with dashboard analytics for tracking active and resolved posts. Users can bookmark posts for later reference and manage their saved collections.

Comment System

Real-time commenting functionality allows users to ask questions, provide updates, or coordinate meetups directly on posts. Comments are rate-limited to prevent spam and support threaded discussions.

Technical Architecture

Technology Stack

Next.jsTypeScriptExpress.jsReactMongoDBRedisCloudinaryLeafletSASSContext-API

Frontend:

  • Next.js 14 with App Router
  • TypeScript for type safety
  • SCSS Modules for component-scoped styling
  • Leaflet.js for interactive maps
  • React Context API for state management

Backend:

  • Express.js with TypeScript
  • Mongoose ODM for MongoDB interactions
  • Redis for session storage and caching
  • Helmet.js for security headers
  • Morgan for request logging
  • express-mongo-sanitize for NoSQL injection prevention
  • Zod for schema validation

Infrastructure:

  • MongoDB Atlas for database hosting
  • Redis Cloud for caching layer
  • Cloudinary for image CDN
  • Vercel for frontend deployment
  • Railway/Render for backend deployment

API Documentation

Base URL

Production: https://api.lostfound.ro/api/v1
Development: http://localhost:8000/api/v1

Authentication Routes (/auth)

MethodEndpointRate LimitDescription
POST/register5/10minCreate new user account with email verification
POST/login10/5minAuthenticate user and issue JWT tokens
POST/logout-Invalidate refresh token and clear cookies
POST/refresh-token-Generate new access token using refresh token
POST/verify-email10/minConfirm email address with verification code
POST/forgot-password10/minRequest password reset email
POST/reset-password10/minReset password using token from email

Authentication Flow:

  1. User registers → Email verification sent
  2. User verifies email → Account activated
  3. User logs in → Access token (15min) + Refresh token (7d) issued
  4. Access token expires → Client requests new token using refresh token
  5. Refresh token expires → User must log in again

Post Management Routes (/post)

MethodEndpointAuthRate LimitDescription
POST/create93/10minCreate new lost/found post with images
GET/:postId-30/minRetrieve single post by ID
PUT/edit/:postId20/5minUpdate post details and images
PATCH/solve/:postId30/minMark post as resolved
DELETE/delete/:postId10/5minDelete user's own post
GET/user-posts30/minGet all posts by authenticated user
GET/latest-30/minFetch recent posts with pagination

Post Creation Example:

POST/api/v1/post/createContent-Type: multipart/form-data
Authorization: Bearer{access_token}{title: "Lost Black Labrador",description: "Last seen near Central Park",category: "pet",type: "lost",location: {lat: 44.4268,lon: 26.1025,display_name: "Bucharest, Romania"},contactInfo: {phone: "+40123456789",email: "contact@example.com"},images: [File,File]// Max 5 images, 5MB each}

User Management Routes (/user)

MethodEndpointAuthRate LimitDescription
GET/profile30/minGet authenticated user's profile
GET/public-profile/:id-30/minView public user profile
PUT/change-password2/minUpdate user password
PUT/change-profile-image2/minUpload new profile picture
DELETE/delete-account2/minPermanently delete user account
GET/saved-posts-Retrieve user's bookmarked posts
POST/save-post30/minBookmark a post
POST/remove-post30/minRemove post from bookmarks

Geocoding Routes (/geo)

MethodEndpointRate LimitDescription
GET/search?q={query}&limit={n}60/minForward geocoding (address → coordinates)
GET/reverse?lat={lat}&lon={lon}60/minReverse geocoding (coordinates → address)
GET/health-Service health check

Geocoding Features:

  • Results cached in Redis for 1 hour
  • Country-specific to Romania (countrycodes=ro)
  • Coordinate validation: lat ∈ [43.5, 48.3], lon ∈ [20.2, 29.7]
  • Automatic language localization (Romanian)
  • Deduplicated results with importance scoring

Comment Routes (/comment)

MethodEndpointAuthRate LimitDescription
POST/create5/minAdd comment to post
DELETE/delete/:commentId5/minDelete own comment

Search Routes (/search)

MethodEndpointDescription
GET/posts?q={query}&category={cat}&location={loc}&radius={km}&dateFrom={date}&dateTo={date}Advanced post search

Search Parameters:

  • q: Text search in title/description
  • category: Filter by category (pet, electronics, documents, etc.)
  • location: Center point for radius search
  • radius: Search radius in kilometers
  • dateFrom/dateTo: Filter by posting date range

Security Implementation

Input Validation & Sanitization

Zod Schema Validation - All incoming requests are validated against TypeScript-first schemas before reaching controllers. This ensures type safety and catches malformed data early in the request lifecycle.

// Example: Post creation schemaconstcreatePostSchema=z.object({title: z.string().min(3).max(100),description: z.string().min(10).max(2000),category: z.enum(['pet','electronics','documents','jewelry','other']),type: z.enum(['lost','found']),location: z.object({lat: z.number().min(43.5).max(48.3),lon: z.number().min(20.2).max(29.7),display_name: z.string()})});

NoSQL Injection Prevention - express-mongo-sanitize middleware strips out $ and . characters from user input, preventing MongoDB operator injection attacks. This protects against malicious queries that attempt to manipulate database operations.

// Sanitization applied globally to all routesapp.use(mongoSanitize());// Example attack prevented:// { "email": { "$gt": "" }} → { "email": "" }

Rate Limiting Architecture

Redis-backed rate limiting prevents abuse and ensures fair resource allocation. Different endpoints have tiered limits based on their resource intensity:

Endpoint TypeWindowLimitRationale
Registration10 min5Prevent bot account creation
Login5 min10Balance security vs. user experience
Post Creation10 min93Allow legitimate use while preventing spam
Image Upload5 min115Protect storage and bandwidth
Geocoding1 min60Respect external API fair use
Comments1 min5Prevent spam without hindering discussion
Profile Updates1 min2Critical operations need strict limits

Rate limit state is stored in Redis with key prefixes (rl_register:, rl_login:, etc.) for namespace isolation. The system returns standardized error responses with retry-after headers compliant with RFC 6585.

Authentication & Authorization

JWT Token Strategy:

  • Access Tokens: Short-lived (15 minutes), contain user ID and role
  • Refresh Tokens: Long-lived (7 days), stored in httpOnly cookies
  • Token Rotation: Each refresh generates new token pair, old tokens invalidated
  • Signature Algorithm: HS256 with secrets ≥32 characters

Cookie Security:

res.cookie('refreshToken',token,{httpOnly: true,// Prevent XSS accesssecure: true,// HTTPS only in productionsameSite: 'strict',// CSRF protectionmaxAge: 7*24*60*60*1000// 7 days});

Password Security:

  • bcrypt hashing with salt rounds = 12
  • Minimum 8 characters with complexity requirements
  • Passwords never logged or returned in responses
  • Secure password reset with time-limited tokens

HTTP Security Headers (Helmet.js)

app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],imgSrc: ["'self'","data:","https://res.cloudinary.com"],scriptSrc: ["'self'","'unsafe-inline'"],// Next.js requirement}},hsts: {maxAge: 31536000,includeSubDomains: true,preload: true}}));

Enabled protections include CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

File Upload Security

Multer Configuration:

  • Memory storage (no disk writes in development)
  • MIME type validation before processing
  • Size limits: 5MB per file, max 5 files per request
  • Allowed formats: JPEG, JPG, PNG, WebP only
  • Error handling for malformed uploads

Cloudinary Integration:

  • Automatic format optimization (WebP conversion)
  • Lazy transformation for responsive images
  • Signed upload URLs prevent unauthorized uploads
  • CDN delivery reduces origin server load

CORS Policy

app.use(cors({origin: process.env.FRONTEND_URL,// Whitelist specific origincredentials: true,// Allow cookiesmethods: ['GET','POST','PUT','PATCH','DELETE'],allowedHeaders: ['Content-Type','Authorization']}));

Strict CORS configuration prevents cross-origin attacks while enabling authenticated requests from the frontend.

Performance Optimizations

Caching Strategy

Redis Caching Layer:

  • Geocoding responses: 1-hour TTL (key: search:{query}:{limit})
  • Reverse geocoding: 1-hour TTL (key: reverse:{lat}:{lon})
  • Rate limit counters: Sliding window with automatic expiration
  • Session tokens: TTL matches JWT expiration

Cache hit rate monitoring shows ~75% cache hits for geocoding queries, reducing external API calls and improving response times from ~800ms to ~15ms.

Database Indexing

MongoDB Indexes:

// Geospatial index for location queriespostSchema.index({location: '2dsphere'});// Compound index for filtered searchespostSchema.index({category: 1,type: 1,createdAt: -1});// Text index for full-text searchpostSchema.index({title: 'text',description: 'text'});// User lookup optimizationpostSchema.index({userId: 1,status: 1});

Query performance benchmarks show 95th percentile latency under 50ms for indexed queries vs. 2000ms+ for full collection scans.

Image Optimization Pipeline

Cloudinary Transformations:

  • Automatic WebP conversion with fallback to original format
  • Responsive image variants (thumbnail, medium, full)
  • Lazy loading with low-quality image placeholders (LQIP)
  • CDN edge caching for global delivery

Optimization Results:

  • Average image size: 2.3MB → 180KB (WebP)
  • Page load time: 4.2s → 1.8s
  • Bandwidth savings: ~92%

Frontend Optimizations

Next.js Features:

  • Automatic code splitting per route
  • Server-side rendering for SEO and initial load performance
  • Static generation for public pages
  • Image component with built-in lazy loading
  • Font optimization with Geist preloading

Bundle Analysis:

  • Initial JS bundle: 142KB gzipped
  • First Contentful Paint: ~1.2s
  • Time to Interactive: ~2.3s
  • Lighthouse Performance Score: 94/100

Technical Challenges

Geospatial Accuracy & Validation

Challenge: Ensuring coordinates are valid and fall within Romania's boundaries while handling edge cases like users near borders or coordinates from external sources.

Solution: Implemented strict Zod validation with min/max constraints on latitude (43.5-48.3°N) and longitude (20.2-29.7°E). Added fallback mechanisms when Nominatim API fails—system gracefully degrades to displaying raw coordinates rather than throwing errors.

constreverseSchema=z.object({lat: z.coerce.number().min(43.5).max(48.3),lon: z.coerce.number().min(20.2).max(29.7)});// Fallback response on API failurecatch(error){res.json({display_name: `${lat.toFixed(5)}, ${lon.toFixed(5)}`,address: {},
lat, lon
});}

Concurrent Update Conflicts

Challenge: Race conditions when multiple users interact with the same post simultaneously (editing, commenting, marking resolved).

Solution: Leveraged MongoDB's atomic update operators ($set, $push, $inc) and implemented optimistic locking with version fields. Critical operations use transactions to ensure data consistency.

// Atomic operation prevents race conditionsawaitPost.findByIdAndUpdate(postId,{$set: {status: 'solved',solvedAt: newDate()}},{new: true,runValidators: true});

External API Resilience

Challenge: Nominatim API rate limits (1 request/second) and occasional timeouts causing user-facing errors.

Solution: Three-layered approach:

  1. Redis caching with 1-hour TTL reduces API calls by ~75%
  2. Timeout configuration (5s) prevents hanging requests
  3. Graceful degradation returns partial data instead of failing

Rate limiting on the geocoding endpoint (60/min) ensures compliance with Nominatim's usage policy while accommodating legitimate user activity.

Scalability & Resource Management

Challenge: As user base grows, managing database connections, Redis connections, and memory usage becomes critical.

Solution:

  • MongoDB connection pooling (min: 10, max: 50 connections)
  • Redis connection reuse with single client instance
  • Image uploads limited to 5MB to prevent memory exhaustion
  • Rate limiting prevents resource starvation from malicious actors
  • Horizontal scaling strategy with load balancer-ready stateless design

Search Performance at Scale

Challenge: Text search across thousands of posts with multiple filters (location, category, date) must remain fast.

Solution: Implemented compound indexes covering common query patterns and MongoDB aggregation pipeline for complex searches. Future optimization plan includes Elasticsearch integration for full-text search once post volume exceeds 100K records.

Mobile Responsive Design

Mobile HomepageMobile PostMobile Map

Fully responsive design with touch-optimized map controls, collapsible filters, and mobile-first form layouts. CSS Grid and Flexbox ensure consistent layouts across devices. Breakpoints at 768px and 1024px accommodate tablets and desktops.

Installation

Prerequisites

  • Node.js 18+ and npm
  • MongoDB 5.0+
  • Redis 6.0+
  • Cloudinary account (free tier sufficient)

Setup Instructions

# Clone repository
git clone https://github.com/Rotis-Web/lostfound.git
cd lostfound
# Install frontend dependenciescd client
npm install
# Install backend dependenciescd ../server
npm install
# Start MongoDB and Redis (if running locally)# macOS with Homebrew:
brew services start mongodb-community
brew services start redis
# Run development servers
npm run dev:all
# This starts both frontend (port 3000) and backend (port 8000)

Environment Configuration

Frontend Configuration

Create client/.env.local:

NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name

Backend Configuration

Create server/.env:

# Server
PORT=8000
NODE_ENV=development
# Database
MONGO_URI=mongodb://localhost:27017/lostfound
# Production: mongodb+srv://username:password@cluster.mongodb.net/lostfound# Redis
REDIS_URL=redis://localhost:6379
# Production: redis://username:password@host:port# Application URLs
APP_ORIGIN=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# JWT Configuration (generate random 32+ char strings)
JWT_SECRET=your_secure_secret_min_32_chars_use_openssl_rand
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret_different_from_above
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary (sign up at cloudinary.com)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_specific_password

Generating Secure Secrets:

# Use OpenSSL to generate random secrets
openssl rand -base64 32

Design Philosophy

The interface prioritizes clarity and accessibility with a warm color palette that conveys hope and urgency. The design system balances vibrant accent colors with professional neutrals to create an approachable yet trustworthy aesthetic.

Color Palette

ColorHexUsage
Yellow Primary#ffd700CTAs, highlights - conveys energy and optimism
Dark Blue#2c3e60Headers, text - inspires trust and professionalism
Orange Accent#f57a4eImportant buttons, alerts - draws attention
Green Success#51e188Success messages, resolved posts
Red Alert#ff4444Error states, urgent actions
Neutral Gray#9ca3afSecondary text, borders, disabled states

Typography

  • Font Family: Geist Sans - Modern, highly legible sans-serif optimized for UI
  • Heading Scale: 2.5rem / 2rem / 1.5rem / 1.25rem / 1rem
  • Body Text: 1rem (16px) with 1.5 line height for optimal readability
  • Code/Monospace: Geist Mono for technical content

Built to reunite people with what matters most

TypeScriptNext.jsExpressMongoDBRedis

License: MIT | Developer: Alexandru Rotar

About

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

194 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🔍 Lost & Found - Reuniting People with Lost Items & Pets

Lost & Found Banner

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching. The platform combines real-time mapping, advanced search algorithms, and secure user authentication to create a comprehensive lost-and-found ecosystem.

Table of Contents

Project Overview

This application addresses the challenge of efficiently connecting people who have lost items with those who have found them. By leveraging geospatial indexing and caching strategies, the platform delivers fast, location-aware search results while maintaining data security and system reliability.

Key Technical Highlights:

  • Geospatial queries with MongoDB 2dsphere indexes
  • Redis-backed rate limiting and caching
  • JWT-based authentication with refresh token rotation
  • Automated image optimization via Cloudinary CDN
  • XSS protection through input sanitization with express-mongo-sanitize
  • Comprehensive input validation using Zod schemas

Preview

Post PageCreate Post
DashboardHomepage

Core Features

Geospatial Posting & Discovery

Users can create posts with precise coordinates using Leaflet.js integration and OpenStreetMap's Nominatim API. The system implements MongoDB geospatial indexes for efficient radius-based queries, enabling users to discover nearby lost/found items within customizable distance ranges.

Advanced Search & Filtering

Multi-parameter search functionality includes text matching, date ranges, categories, and location-based filtering. Search queries are optimized through Redis caching with intelligent TTL management (1-hour expiration), reducing external API calls and improving response times.

Printable Flyer Generator

Automated generation of professional PDF flyers with QR codes linking back to the online post. Templates are optimized for A4 printing and include customizable layouts that adapt to different item types.

User Management System

Secure authentication flow with JWT tokens (access + refresh), email verification, and password recovery. User profiles maintain posting history with dashboard analytics for tracking active and resolved posts. Users can bookmark posts for later reference and manage their saved collections.

Comment System

Real-time commenting functionality allows users to ask questions, provide updates, or coordinate meetups directly on posts. Comments are rate-limited to prevent spam and support threaded discussions.

Technical Architecture

Technology Stack

Next.jsTypeScriptExpress.jsReactMongoDBRedisCloudinaryLeafletSASSContext-API

Frontend:

  • Next.js 14 with App Router
  • TypeScript for type safety
  • SCSS Modules for component-scoped styling
  • Leaflet.js for interactive maps
  • React Context API for state management

Backend:

  • Express.js with TypeScript
  • Mongoose ODM for MongoDB interactions
  • Redis for session storage and caching
  • Helmet.js for security headers
  • Morgan for request logging
  • express-mongo-sanitize for NoSQL injection prevention
  • Zod for schema validation

Infrastructure:

  • MongoDB Atlas for database hosting
  • Redis Cloud for caching layer
  • Cloudinary for image CDN
  • Vercel for frontend deployment
  • Railway/Render for backend deployment

API Documentation

Base URL

Production: https://api.lostfound.ro/api/v1
Development: http://localhost:8000/api/v1

Authentication Routes (/auth)

MethodEndpointRate LimitDescription
POST/register5/10minCreate new user account with email verification
POST/login10/5minAuthenticate user and issue JWT tokens
POST/logout-Invalidate refresh token and clear cookies
POST/refresh-token-Generate new access token using refresh token
POST/verify-email10/minConfirm email address with verification code
POST/forgot-password10/minRequest password reset email
POST/reset-password10/minReset password using token from email

Authentication Flow:

  1. User registers → Email verification sent
  2. User verifies email → Account activated
  3. User logs in → Access token (15min) + Refresh token (7d) issued
  4. Access token expires → Client requests new token using refresh token
  5. Refresh token expires → User must log in again

Post Management Routes (/post)

MethodEndpointAuthRate LimitDescription
POST/create93/10minCreate new lost/found post with images
GET/:postId-30/minRetrieve single post by ID
PUT/edit/:postId20/5minUpdate post details and images
PATCH/solve/:postId30/minMark post as resolved
DELETE/delete/:postId10/5minDelete user's own post
GET/user-posts30/minGet all posts by authenticated user
GET/latest-30/minFetch recent posts with pagination

Post Creation Example:

POST/api/v1/post/createContent-Type: multipart/form-data
Authorization: Bearer{access_token}{title: "Lost Black Labrador",description: "Last seen near Central Park",category: "pet",type: "lost",location: {lat: 44.4268,lon: 26.1025,display_name: "Bucharest, Romania"},contactInfo: {phone: "+40123456789",email: "contact@example.com"},images: [File,File]// Max 5 images, 5MB each}

User Management Routes (/user)

MethodEndpointAuthRate LimitDescription
GET/profile30/minGet authenticated user's profile
GET/public-profile/:id-30/minView public user profile
PUT/change-password2/minUpdate user password
PUT/change-profile-image2/minUpload new profile picture
DELETE/delete-account2/minPermanently delete user account
GET/saved-posts-Retrieve user's bookmarked posts
POST/save-post30/minBookmark a post
POST/remove-post30/minRemove post from bookmarks

Geocoding Routes (/geo)

MethodEndpointRate LimitDescription
GET/search?q={query}&limit={n}60/minForward geocoding (address → coordinates)
GET/reverse?lat={lat}&lon={lon}60/minReverse geocoding (coordinates → address)
GET/health-Service health check

Geocoding Features:

  • Results cached in Redis for 1 hour
  • Country-specific to Romania (countrycodes=ro)
  • Coordinate validation: lat ∈ [43.5, 48.3], lon ∈ [20.2, 29.7]
  • Automatic language localization (Romanian)
  • Deduplicated results with importance scoring

Comment Routes (/comment)

MethodEndpointAuthRate LimitDescription
POST/create5/minAdd comment to post
DELETE/delete/:commentId5/minDelete own comment

Search Routes (/search)

MethodEndpointDescription
GET/posts?q={query}&category={cat}&location={loc}&radius={km}&dateFrom={date}&dateTo={date}Advanced post search

Search Parameters:

  • q: Text search in title/description
  • category: Filter by category (pet, electronics, documents, etc.)
  • location: Center point for radius search
  • radius: Search radius in kilometers
  • dateFrom/dateTo: Filter by posting date range

Security Implementation

Input Validation & Sanitization

Zod Schema Validation - All incoming requests are validated against TypeScript-first schemas before reaching controllers. This ensures type safety and catches malformed data early in the request lifecycle.

// Example: Post creation schemaconstcreatePostSchema=z.object({title: z.string().min(3).max(100),description: z.string().min(10).max(2000),category: z.enum(['pet','electronics','documents','jewelry','other']),type: z.enum(['lost','found']),location: z.object({lat: z.number().min(43.5).max(48.3),lon: z.number().min(20.2).max(29.7),display_name: z.string()})});

NoSQL Injection Prevention - express-mongo-sanitize middleware strips out $ and . characters from user input, preventing MongoDB operator injection attacks. This protects against malicious queries that attempt to manipulate database operations.

// Sanitization applied globally to all routesapp.use(mongoSanitize());// Example attack prevented:// { "email": { "$gt": "" }} → { "email": "" }

Rate Limiting Architecture

Redis-backed rate limiting prevents abuse and ensures fair resource allocation. Different endpoints have tiered limits based on their resource intensity:

Endpoint TypeWindowLimitRationale
Registration10 min5Prevent bot account creation
Login5 min10Balance security vs. user experience
Post Creation10 min93Allow legitimate use while preventing spam
Image Upload5 min115Protect storage and bandwidth
Geocoding1 min60Respect external API fair use
Comments1 min5Prevent spam without hindering discussion
Profile Updates1 min2Critical operations need strict limits

Rate limit state is stored in Redis with key prefixes (rl_register:, rl_login:, etc.) for namespace isolation. The system returns standardized error responses with retry-after headers compliant with RFC 6585.

Authentication & Authorization

JWT Token Strategy:

  • Access Tokens: Short-lived (15 minutes), contain user ID and role
  • Refresh Tokens: Long-lived (7 days), stored in httpOnly cookies
  • Token Rotation: Each refresh generates new token pair, old tokens invalidated
  • Signature Algorithm: HS256 with secrets ≥32 characters

Cookie Security:

res.cookie('refreshToken',token,{httpOnly: true,// Prevent XSS accesssecure: true,// HTTPS only in productionsameSite: 'strict',// CSRF protectionmaxAge: 7*24*60*60*1000// 7 days});

Password Security:

  • bcrypt hashing with salt rounds = 12
  • Minimum 8 characters with complexity requirements
  • Passwords never logged or returned in responses
  • Secure password reset with time-limited tokens

HTTP Security Headers (Helmet.js)

app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],imgSrc: ["'self'","data:","https://res.cloudinary.com"],scriptSrc: ["'self'","'unsafe-inline'"],// Next.js requirement}},hsts: {maxAge: 31536000,includeSubDomains: true,preload: true}}));

Enabled protections include CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

File Upload Security

Multer Configuration:

  • Memory storage (no disk writes in development)
  • MIME type validation before processing
  • Size limits: 5MB per file, max 5 files per request
  • Allowed formats: JPEG, JPG, PNG, WebP only
  • Error handling for malformed uploads

Cloudinary Integration:

  • Automatic format optimization (WebP conversion)
  • Lazy transformation for responsive images
  • Signed upload URLs prevent unauthorized uploads
  • CDN delivery reduces origin server load

CORS Policy

app.use(cors({origin: process.env.FRONTEND_URL,// Whitelist specific origincredentials: true,// Allow cookiesmethods: ['GET','POST','PUT','PATCH','DELETE'],allowedHeaders: ['Content-Type','Authorization']}));

Strict CORS configuration prevents cross-origin attacks while enabling authenticated requests from the frontend.

Performance Optimizations

Caching Strategy

Redis Caching Layer:

  • Geocoding responses: 1-hour TTL (key: search:{query}:{limit})
  • Reverse geocoding: 1-hour TTL (key: reverse:{lat}:{lon})
  • Rate limit counters: Sliding window with automatic expiration
  • Session tokens: TTL matches JWT expiration

Cache hit rate monitoring shows ~75% cache hits for geocoding queries, reducing external API calls and improving response times from ~800ms to ~15ms.

Database Indexing

MongoDB Indexes:

// Geospatial index for location queriespostSchema.index({location: '2dsphere'});// Compound index for filtered searchespostSchema.index({category: 1,type: 1,createdAt: -1});// Text index for full-text searchpostSchema.index({title: 'text',description: 'text'});// User lookup optimizationpostSchema.index({userId: 1,status: 1});

Query performance benchmarks show 95th percentile latency under 50ms for indexed queries vs. 2000ms+ for full collection scans.

Image Optimization Pipeline

Cloudinary Transformations:

  • Automatic WebP conversion with fallback to original format
  • Responsive image variants (thumbnail, medium, full)
  • Lazy loading with low-quality image placeholders (LQIP)
  • CDN edge caching for global delivery

Optimization Results:

  • Average image size: 2.3MB → 180KB (WebP)
  • Page load time: 4.2s → 1.8s
  • Bandwidth savings: ~92%

Frontend Optimizations

Next.js Features:

  • Automatic code splitting per route
  • Server-side rendering for SEO and initial load performance
  • Static generation for public pages
  • Image component with built-in lazy loading
  • Font optimization with Geist preloading

Bundle Analysis:

  • Initial JS bundle: 142KB gzipped
  • First Contentful Paint: ~1.2s
  • Time to Interactive: ~2.3s
  • Lighthouse Performance Score: 94/100

Technical Challenges

Geospatial Accuracy & Validation

Challenge: Ensuring coordinates are valid and fall within Romania's boundaries while handling edge cases like users near borders or coordinates from external sources.

Solution: Implemented strict Zod validation with min/max constraints on latitude (43.5-48.3°N) and longitude (20.2-29.7°E). Added fallback mechanisms when Nominatim API fails—system gracefully degrades to displaying raw coordinates rather than throwing errors.

constreverseSchema=z.object({lat: z.coerce.number().min(43.5).max(48.3),lon: z.coerce.number().min(20.2).max(29.7)});// Fallback response on API failurecatch(error){res.json({display_name: `${lat.toFixed(5)}, ${lon.toFixed(5)}`,address: {},
lat, lon
});}

Concurrent Update Conflicts

Challenge: Race conditions when multiple users interact with the same post simultaneously (editing, commenting, marking resolved).

Solution: Leveraged MongoDB's atomic update operators ($set, $push, $inc) and implemented optimistic locking with version fields. Critical operations use transactions to ensure data consistency.

// Atomic operation prevents race conditionsawaitPost.findByIdAndUpdate(postId,{$set: {status: 'solved',solvedAt: newDate()}},{new: true,runValidators: true});

External API Resilience

Challenge: Nominatim API rate limits (1 request/second) and occasional timeouts causing user-facing errors.

Solution: Three-layered approach:

  1. Redis caching with 1-hour TTL reduces API calls by ~75%
  2. Timeout configuration (5s) prevents hanging requests
  3. Graceful degradation returns partial data instead of failing

Rate limiting on the geocoding endpoint (60/min) ensures compliance with Nominatim's usage policy while accommodating legitimate user activity.

Scalability & Resource Management

Challenge: As user base grows, managing database connections, Redis connections, and memory usage becomes critical.

Solution:

  • MongoDB connection pooling (min: 10, max: 50 connections)
  • Redis connection reuse with single client instance
  • Image uploads limited to 5MB to prevent memory exhaustion
  • Rate limiting prevents resource starvation from malicious actors
  • Horizontal scaling strategy with load balancer-ready stateless design

Search Performance at Scale

Challenge: Text search across thousands of posts with multiple filters (location, category, date) must remain fast.

Solution: Implemented compound indexes covering common query patterns and MongoDB aggregation pipeline for complex searches. Future optimization plan includes Elasticsearch integration for full-text search once post volume exceeds 100K records.

Mobile Responsive Design

Mobile HomepageMobile PostMobile Map

Fully responsive design with touch-optimized map controls, collapsible filters, and mobile-first form layouts. CSS Grid and Flexbox ensure consistent layouts across devices. Breakpoints at 768px and 1024px accommodate tablets and desktops.

Installation

Prerequisites

  • Node.js 18+ and npm
  • MongoDB 5.0+
  • Redis 6.0+
  • Cloudinary account (free tier sufficient)

Setup Instructions

# Clone repository
git clone https://github.com/Rotis-Web/lostfound.git
cd lostfound
# Install frontend dependenciescd client
npm install
# Install backend dependenciescd ../server
npm install
# Start MongoDB and Redis (if running locally)# macOS with Homebrew:
brew services start mongodb-community
brew services start redis
# Run development servers
npm run dev:all
# This starts both frontend (port 3000) and backend (port 8000)

Environment Configuration

Frontend Configuration

Create client/.env.local:

NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name

Backend Configuration

Create server/.env:

# Server
PORT=8000
NODE_ENV=development
# Database
MONGO_URI=mongodb://localhost:27017/lostfound
# Production: mongodb+srv://username:password@cluster.mongodb.net/lostfound# Redis
REDIS_URL=redis://localhost:6379
# Production: redis://username:password@host:port# Application URLs
APP_ORIGIN=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# JWT Configuration (generate random 32+ char strings)
JWT_SECRET=your_secure_secret_min_32_chars_use_openssl_rand
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret_different_from_above
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary (sign up at cloudinary.com)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_specific_password

Generating Secure Secrets:

# Use OpenSSL to generate random secrets
openssl rand -base64 32

Design Philosophy

The interface prioritizes clarity and accessibility with a warm color palette that conveys hope and urgency. The design system balances vibrant accent colors with professional neutrals to create an approachable yet trustworthy aesthetic.

Color Palette

ColorHexUsage
Yellow Primary#ffd700CTAs, highlights - conveys energy and optimism
Dark Blue#2c3e60Headers, text - inspires trust and professionalism
Orange Accent#f57a4eImportant buttons, alerts - draws attention
Green Success#51e188Success messages, resolved posts
Red Alert#ff4444Error states, urgent actions
Neutral Gray#9ca3afSecondary text, borders, disabled states

Typography

  • Font Family: Geist Sans - Modern, highly legible sans-serif optimized for UI
  • Heading Scale: 2.5rem / 2rem / 1.5rem / 1.25rem / 1rem
  • Body Text: 1rem (16px) with 1.5 line height for optimal readability
  • Code/Monospace: Geist Mono for technical content

Built to reunite people with what matters most

TypeScriptNext.jsExpressMongoDBRedis

License: MIT | Developer: Alexandru Rotar

About

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

194 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🔍 Lost & Found - Reuniting People with Lost Items & Pets

Lost & Found Banner

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching. The platform combines real-time mapping, advanced search algorithms, and secure user authentication to create a comprehensive lost-and-found ecosystem.

Table of Contents

Project Overview

This application addresses the challenge of efficiently connecting people who have lost items with those who have found them. By leveraging geospatial indexing and caching strategies, the platform delivers fast, location-aware search results while maintaining data security and system reliability.

Key Technical Highlights:

  • Geospatial queries with MongoDB 2dsphere indexes
  • Redis-backed rate limiting and caching
  • JWT-based authentication with refresh token rotation
  • Automated image optimization via Cloudinary CDN
  • XSS protection through input sanitization with express-mongo-sanitize
  • Comprehensive input validation using Zod schemas

Preview

Post PageCreate Post
DashboardHomepage

Core Features

Geospatial Posting & Discovery

Users can create posts with precise coordinates using Leaflet.js integration and OpenStreetMap's Nominatim API. The system implements MongoDB geospatial indexes for efficient radius-based queries, enabling users to discover nearby lost/found items within customizable distance ranges.

Advanced Search & Filtering

Multi-parameter search functionality includes text matching, date ranges, categories, and location-based filtering. Search queries are optimized through Redis caching with intelligent TTL management (1-hour expiration), reducing external API calls and improving response times.

Printable Flyer Generator

Automated generation of professional PDF flyers with QR codes linking back to the online post. Templates are optimized for A4 printing and include customizable layouts that adapt to different item types.

User Management System

Secure authentication flow with JWT tokens (access + refresh), email verification, and password recovery. User profiles maintain posting history with dashboard analytics for tracking active and resolved posts. Users can bookmark posts for later reference and manage their saved collections.

Comment System

Real-time commenting functionality allows users to ask questions, provide updates, or coordinate meetups directly on posts. Comments are rate-limited to prevent spam and support threaded discussions.

Technical Architecture

Technology Stack

Next.jsTypeScriptExpress.jsReactMongoDBRedisCloudinaryLeafletSASSContext-API

Frontend:

  • Next.js 14 with App Router
  • TypeScript for type safety
  • SCSS Modules for component-scoped styling
  • Leaflet.js for interactive maps
  • React Context API for state management

Backend:

  • Express.js with TypeScript
  • Mongoose ODM for MongoDB interactions
  • Redis for session storage and caching
  • Helmet.js for security headers
  • Morgan for request logging
  • express-mongo-sanitize for NoSQL injection prevention
  • Zod for schema validation

Infrastructure:

  • MongoDB Atlas for database hosting
  • Redis Cloud for caching layer
  • Cloudinary for image CDN
  • Vercel for frontend deployment
  • Railway/Render for backend deployment

API Documentation

Base URL

Production: https://api.lostfound.ro/api/v1
Development: http://localhost:8000/api/v1

Authentication Routes (/auth)

MethodEndpointRate LimitDescription
POST/register5/10minCreate new user account with email verification
POST/login10/5minAuthenticate user and issue JWT tokens
POST/logout-Invalidate refresh token and clear cookies
POST/refresh-token-Generate new access token using refresh token
POST/verify-email10/minConfirm email address with verification code
POST/forgot-password10/minRequest password reset email
POST/reset-password10/minReset password using token from email

Authentication Flow:

  1. User registers → Email verification sent
  2. User verifies email → Account activated
  3. User logs in → Access token (15min) + Refresh token (7d) issued
  4. Access token expires → Client requests new token using refresh token
  5. Refresh token expires → User must log in again

Post Management Routes (/post)

MethodEndpointAuthRate LimitDescription
POST/create93/10minCreate new lost/found post with images
GET/:postId-30/minRetrieve single post by ID
PUT/edit/:postId20/5minUpdate post details and images
PATCH/solve/:postId30/minMark post as resolved
DELETE/delete/:postId10/5minDelete user's own post
GET/user-posts30/minGet all posts by authenticated user
GET/latest-30/minFetch recent posts with pagination

Post Creation Example:

POST/api/v1/post/createContent-Type: multipart/form-data
Authorization: Bearer{access_token}{title: "Lost Black Labrador",description: "Last seen near Central Park",category: "pet",type: "lost",location: {lat: 44.4268,lon: 26.1025,display_name: "Bucharest, Romania"},contactInfo: {phone: "+40123456789",email: "contact@example.com"},images: [File,File]// Max 5 images, 5MB each}

User Management Routes (/user)

MethodEndpointAuthRate LimitDescription
GET/profile30/minGet authenticated user's profile
GET/public-profile/:id-30/minView public user profile
PUT/change-password2/minUpdate user password
PUT/change-profile-image2/minUpload new profile picture
DELETE/delete-account2/minPermanently delete user account
GET/saved-posts-Retrieve user's bookmarked posts
POST/save-post30/minBookmark a post
POST/remove-post30/minRemove post from bookmarks

Geocoding Routes (/geo)

MethodEndpointRate LimitDescription
GET/search?q={query}&limit={n}60/minForward geocoding (address → coordinates)
GET/reverse?lat={lat}&lon={lon}60/minReverse geocoding (coordinates → address)
GET/health-Service health check

Geocoding Features:

  • Results cached in Redis for 1 hour
  • Country-specific to Romania (countrycodes=ro)
  • Coordinate validation: lat ∈ [43.5, 48.3], lon ∈ [20.2, 29.7]
  • Automatic language localization (Romanian)
  • Deduplicated results with importance scoring

Comment Routes (/comment)

MethodEndpointAuthRate LimitDescription
POST/create5/minAdd comment to post
DELETE/delete/:commentId5/minDelete own comment

Search Routes (/search)

MethodEndpointDescription
GET/posts?q={query}&category={cat}&location={loc}&radius={km}&dateFrom={date}&dateTo={date}Advanced post search

Search Parameters:

  • q: Text search in title/description
  • category: Filter by category (pet, electronics, documents, etc.)
  • location: Center point for radius search
  • radius: Search radius in kilometers
  • dateFrom/dateTo: Filter by posting date range

Security Implementation

Input Validation & Sanitization

Zod Schema Validation - All incoming requests are validated against TypeScript-first schemas before reaching controllers. This ensures type safety and catches malformed data early in the request lifecycle.

// Example: Post creation schemaconstcreatePostSchema=z.object({title: z.string().min(3).max(100),description: z.string().min(10).max(2000),category: z.enum(['pet','electronics','documents','jewelry','other']),type: z.enum(['lost','found']),location: z.object({lat: z.number().min(43.5).max(48.3),lon: z.number().min(20.2).max(29.7),display_name: z.string()})});

NoSQL Injection Prevention - express-mongo-sanitize middleware strips out $ and . characters from user input, preventing MongoDB operator injection attacks. This protects against malicious queries that attempt to manipulate database operations.

// Sanitization applied globally to all routesapp.use(mongoSanitize());// Example attack prevented:// { "email": { "$gt": "" }} → { "email": "" }

Rate Limiting Architecture

Redis-backed rate limiting prevents abuse and ensures fair resource allocation. Different endpoints have tiered limits based on their resource intensity:

Endpoint TypeWindowLimitRationale
Registration10 min5Prevent bot account creation
Login5 min10Balance security vs. user experience
Post Creation10 min93Allow legitimate use while preventing spam
Image Upload5 min115Protect storage and bandwidth
Geocoding1 min60Respect external API fair use
Comments1 min5Prevent spam without hindering discussion
Profile Updates1 min2Critical operations need strict limits

Rate limit state is stored in Redis with key prefixes (rl_register:, rl_login:, etc.) for namespace isolation. The system returns standardized error responses with retry-after headers compliant with RFC 6585.

Authentication & Authorization

JWT Token Strategy:

  • Access Tokens: Short-lived (15 minutes), contain user ID and role
  • Refresh Tokens: Long-lived (7 days), stored in httpOnly cookies
  • Token Rotation: Each refresh generates new token pair, old tokens invalidated
  • Signature Algorithm: HS256 with secrets ≥32 characters

Cookie Security:

res.cookie('refreshToken',token,{httpOnly: true,// Prevent XSS accesssecure: true,// HTTPS only in productionsameSite: 'strict',// CSRF protectionmaxAge: 7*24*60*60*1000// 7 days});

Password Security:

  • bcrypt hashing with salt rounds = 12
  • Minimum 8 characters with complexity requirements
  • Passwords never logged or returned in responses
  • Secure password reset with time-limited tokens

HTTP Security Headers (Helmet.js)

app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],imgSrc: ["'self'","data:","https://res.cloudinary.com"],scriptSrc: ["'self'","'unsafe-inline'"],// Next.js requirement}},hsts: {maxAge: 31536000,includeSubDomains: true,preload: true}}));

Enabled protections include CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

File Upload Security

Multer Configuration:

  • Memory storage (no disk writes in development)
  • MIME type validation before processing
  • Size limits: 5MB per file, max 5 files per request
  • Allowed formats: JPEG, JPG, PNG, WebP only
  • Error handling for malformed uploads

Cloudinary Integration:

  • Automatic format optimization (WebP conversion)
  • Lazy transformation for responsive images
  • Signed upload URLs prevent unauthorized uploads
  • CDN delivery reduces origin server load

CORS Policy

app.use(cors({origin: process.env.FRONTEND_URL,// Whitelist specific origincredentials: true,// Allow cookiesmethods: ['GET','POST','PUT','PATCH','DELETE'],allowedHeaders: ['Content-Type','Authorization']}));

Strict CORS configuration prevents cross-origin attacks while enabling authenticated requests from the frontend.

Performance Optimizations

Caching Strategy

Redis Caching Layer:

  • Geocoding responses: 1-hour TTL (key: search:{query}:{limit})
  • Reverse geocoding: 1-hour TTL (key: reverse:{lat}:{lon})
  • Rate limit counters: Sliding window with automatic expiration
  • Session tokens: TTL matches JWT expiration

Cache hit rate monitoring shows ~75% cache hits for geocoding queries, reducing external API calls and improving response times from ~800ms to ~15ms.

Database Indexing

MongoDB Indexes:

// Geospatial index for location queriespostSchema.index({location: '2dsphere'});// Compound index for filtered searchespostSchema.index({category: 1,type: 1,createdAt: -1});// Text index for full-text searchpostSchema.index({title: 'text',description: 'text'});// User lookup optimizationpostSchema.index({userId: 1,status: 1});

Query performance benchmarks show 95th percentile latency under 50ms for indexed queries vs. 2000ms+ for full collection scans.

Image Optimization Pipeline

Cloudinary Transformations:

  • Automatic WebP conversion with fallback to original format
  • Responsive image variants (thumbnail, medium, full)
  • Lazy loading with low-quality image placeholders (LQIP)
  • CDN edge caching for global delivery

Optimization Results:

  • Average image size: 2.3MB → 180KB (WebP)
  • Page load time: 4.2s → 1.8s
  • Bandwidth savings: ~92%

Frontend Optimizations

Next.js Features:

  • Automatic code splitting per route
  • Server-side rendering for SEO and initial load performance
  • Static generation for public pages
  • Image component with built-in lazy loading
  • Font optimization with Geist preloading

Bundle Analysis:

  • Initial JS bundle: 142KB gzipped
  • First Contentful Paint: ~1.2s
  • Time to Interactive: ~2.3s
  • Lighthouse Performance Score: 94/100

Technical Challenges

Geospatial Accuracy & Validation

Challenge: Ensuring coordinates are valid and fall within Romania's boundaries while handling edge cases like users near borders or coordinates from external sources.

Solution: Implemented strict Zod validation with min/max constraints on latitude (43.5-48.3°N) and longitude (20.2-29.7°E). Added fallback mechanisms when Nominatim API fails—system gracefully degrades to displaying raw coordinates rather than throwing errors.

constreverseSchema=z.object({lat: z.coerce.number().min(43.5).max(48.3),lon: z.coerce.number().min(20.2).max(29.7)});// Fallback response on API failurecatch(error){res.json({display_name: `${lat.toFixed(5)}, ${lon.toFixed(5)}`,address: {},
lat, lon
});}

Concurrent Update Conflicts

Challenge: Race conditions when multiple users interact with the same post simultaneously (editing, commenting, marking resolved).

Solution: Leveraged MongoDB's atomic update operators ($set, $push, $inc) and implemented optimistic locking with version fields. Critical operations use transactions to ensure data consistency.

// Atomic operation prevents race conditionsawaitPost.findByIdAndUpdate(postId,{$set: {status: 'solved',solvedAt: newDate()}},{new: true,runValidators: true});

External API Resilience

Challenge: Nominatim API rate limits (1 request/second) and occasional timeouts causing user-facing errors.

Solution: Three-layered approach:

  1. Redis caching with 1-hour TTL reduces API calls by ~75%
  2. Timeout configuration (5s) prevents hanging requests
  3. Graceful degradation returns partial data instead of failing

Rate limiting on the geocoding endpoint (60/min) ensures compliance with Nominatim's usage policy while accommodating legitimate user activity.

Scalability & Resource Management

Challenge: As user base grows, managing database connections, Redis connections, and memory usage becomes critical.

Solution:

  • MongoDB connection pooling (min: 10, max: 50 connections)
  • Redis connection reuse with single client instance
  • Image uploads limited to 5MB to prevent memory exhaustion
  • Rate limiting prevents resource starvation from malicious actors
  • Horizontal scaling strategy with load balancer-ready stateless design

Search Performance at Scale

Challenge: Text search across thousands of posts with multiple filters (location, category, date) must remain fast.

Solution: Implemented compound indexes covering common query patterns and MongoDB aggregation pipeline for complex searches. Future optimization plan includes Elasticsearch integration for full-text search once post volume exceeds 100K records.

Mobile Responsive Design

Mobile HomepageMobile PostMobile Map

Fully responsive design with touch-optimized map controls, collapsible filters, and mobile-first form layouts. CSS Grid and Flexbox ensure consistent layouts across devices. Breakpoints at 768px and 1024px accommodate tablets and desktops.

Installation

Prerequisites

  • Node.js 18+ and npm
  • MongoDB 5.0+
  • Redis 6.0+
  • Cloudinary account (free tier sufficient)

Setup Instructions

# Clone repository
git clone https://github.com/Rotis-Web/lostfound.git
cd lostfound
# Install frontend dependenciescd client
npm install
# Install backend dependenciescd ../server
npm install
# Start MongoDB and Redis (if running locally)# macOS with Homebrew:
brew services start mongodb-community
brew services start redis
# Run development servers
npm run dev:all
# This starts both frontend (port 3000) and backend (port 8000)

Environment Configuration

Frontend Configuration

Create client/.env.local:

NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name

Backend Configuration

Create server/.env:

# Server
PORT=8000
NODE_ENV=development
# Database
MONGO_URI=mongodb://localhost:27017/lostfound
# Production: mongodb+srv://username:password@cluster.mongodb.net/lostfound# Redis
REDIS_URL=redis://localhost:6379
# Production: redis://username:password@host:port# Application URLs
APP_ORIGIN=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# JWT Configuration (generate random 32+ char strings)
JWT_SECRET=your_secure_secret_min_32_chars_use_openssl_rand
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret_different_from_above
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary (sign up at cloudinary.com)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_specific_password

Generating Secure Secrets:

# Use OpenSSL to generate random secrets
openssl rand -base64 32

Design Philosophy

The interface prioritizes clarity and accessibility with a warm color palette that conveys hope and urgency. The design system balances vibrant accent colors with professional neutrals to create an approachable yet trustworthy aesthetic.

Color Palette

ColorHexUsage
Yellow Primary#ffd700CTAs, highlights - conveys energy and optimism
Dark Blue#2c3e60Headers, text - inspires trust and professionalism
Orange Accent#f57a4eImportant buttons, alerts - draws attention
Green Success#51e188Success messages, resolved posts
Red Alert#ff4444Error states, urgent actions
Neutral Gray#9ca3afSecondary text, borders, disabled states

Typography

  • Font Family: Geist Sans - Modern, highly legible sans-serif optimized for UI
  • Heading Scale: 2.5rem / 2rem / 1.5rem / 1.25rem / 1rem
  • Body Text: 1rem (16px) with 1.5 line height for optimal readability
  • Code/Monospace: Geist Mono for technical content

Built to reunite people with what matters most

TypeScriptNext.jsExpressMongoDBRedis

License: MIT | Developer: Alexandru Rotar

About

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

194 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🔍 Lost & Found - Reuniting People with Lost Items & Pets

Lost & Found Banner

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching. The platform combines real-time mapping, advanced search algorithms, and secure user authentication to create a comprehensive lost-and-found ecosystem.

Table of Contents

Project Overview

This application addresses the challenge of efficiently connecting people who have lost items with those who have found them. By leveraging geospatial indexing and caching strategies, the platform delivers fast, location-aware search results while maintaining data security and system reliability.

Key Technical Highlights:

  • Geospatial queries with MongoDB 2dsphere indexes
  • Redis-backed rate limiting and caching
  • JWT-based authentication with refresh token rotation
  • Automated image optimization via Cloudinary CDN
  • XSS protection through input sanitization with express-mongo-sanitize
  • Comprehensive input validation using Zod schemas

Preview

Post PageCreate Post
DashboardHomepage

Core Features

Geospatial Posting & Discovery

Users can create posts with precise coordinates using Leaflet.js integration and OpenStreetMap's Nominatim API. The system implements MongoDB geospatial indexes for efficient radius-based queries, enabling users to discover nearby lost/found items within customizable distance ranges.

Advanced Search & Filtering

Multi-parameter search functionality includes text matching, date ranges, categories, and location-based filtering. Search queries are optimized through Redis caching with intelligent TTL management (1-hour expiration), reducing external API calls and improving response times.

Printable Flyer Generator

Automated generation of professional PDF flyers with QR codes linking back to the online post. Templates are optimized for A4 printing and include customizable layouts that adapt to different item types.

User Management System

Secure authentication flow with JWT tokens (access + refresh), email verification, and password recovery. User profiles maintain posting history with dashboard analytics for tracking active and resolved posts. Users can bookmark posts for later reference and manage their saved collections.

Comment System

Real-time commenting functionality allows users to ask questions, provide updates, or coordinate meetups directly on posts. Comments are rate-limited to prevent spam and support threaded discussions.

Technical Architecture

Technology Stack

Next.jsTypeScriptExpress.jsReactMongoDBRedisCloudinaryLeafletSASSContext-API

Frontend:

  • Next.js 14 with App Router
  • TypeScript for type safety
  • SCSS Modules for component-scoped styling
  • Leaflet.js for interactive maps
  • React Context API for state management

Backend:

  • Express.js with TypeScript
  • Mongoose ODM for MongoDB interactions
  • Redis for session storage and caching
  • Helmet.js for security headers
  • Morgan for request logging
  • express-mongo-sanitize for NoSQL injection prevention
  • Zod for schema validation

Infrastructure:

  • MongoDB Atlas for database hosting
  • Redis Cloud for caching layer
  • Cloudinary for image CDN
  • Vercel for frontend deployment
  • Railway/Render for backend deployment

API Documentation

Base URL

Production: https://api.lostfound.ro/api/v1
Development: http://localhost:8000/api/v1

Authentication Routes (/auth)

MethodEndpointRate LimitDescription
POST/register5/10minCreate new user account with email verification
POST/login10/5minAuthenticate user and issue JWT tokens
POST/logout-Invalidate refresh token and clear cookies
POST/refresh-token-Generate new access token using refresh token
POST/verify-email10/minConfirm email address with verification code
POST/forgot-password10/minRequest password reset email
POST/reset-password10/minReset password using token from email

Authentication Flow:

  1. User registers → Email verification sent
  2. User verifies email → Account activated
  3. User logs in → Access token (15min) + Refresh token (7d) issued
  4. Access token expires → Client requests new token using refresh token
  5. Refresh token expires → User must log in again

Post Management Routes (/post)

MethodEndpointAuthRate LimitDescription
POST/create93/10minCreate new lost/found post with images
GET/:postId-30/minRetrieve single post by ID
PUT/edit/:postId20/5minUpdate post details and images
PATCH/solve/:postId30/minMark post as resolved
DELETE/delete/:postId10/5minDelete user's own post
GET/user-posts30/minGet all posts by authenticated user
GET/latest-30/minFetch recent posts with pagination

Post Creation Example:

POST/api/v1/post/createContent-Type: multipart/form-data
Authorization: Bearer{access_token}{title: "Lost Black Labrador",description: "Last seen near Central Park",category: "pet",type: "lost",location: {lat: 44.4268,lon: 26.1025,display_name: "Bucharest, Romania"},contactInfo: {phone: "+40123456789",email: "contact@example.com"},images: [File,File]// Max 5 images, 5MB each}

User Management Routes (/user)

MethodEndpointAuthRate LimitDescription
GET/profile30/minGet authenticated user's profile
GET/public-profile/:id-30/minView public user profile
PUT/change-password2/minUpdate user password
PUT/change-profile-image2/minUpload new profile picture
DELETE/delete-account2/minPermanently delete user account
GET/saved-posts-Retrieve user's bookmarked posts
POST/save-post30/minBookmark a post
POST/remove-post30/minRemove post from bookmarks

Geocoding Routes (/geo)

MethodEndpointRate LimitDescription
GET/search?q={query}&limit={n}60/minForward geocoding (address → coordinates)
GET/reverse?lat={lat}&lon={lon}60/minReverse geocoding (coordinates → address)
GET/health-Service health check

Geocoding Features:

  • Results cached in Redis for 1 hour
  • Country-specific to Romania (countrycodes=ro)
  • Coordinate validation: lat ∈ [43.5, 48.3], lon ∈ [20.2, 29.7]
  • Automatic language localization (Romanian)
  • Deduplicated results with importance scoring

Comment Routes (/comment)

MethodEndpointAuthRate LimitDescription
POST/create5/minAdd comment to post
DELETE/delete/:commentId5/minDelete own comment

Search Routes (/search)

MethodEndpointDescription
GET/posts?q={query}&category={cat}&location={loc}&radius={km}&dateFrom={date}&dateTo={date}Advanced post search

Search Parameters:

  • q: Text search in title/description
  • category: Filter by category (pet, electronics, documents, etc.)
  • location: Center point for radius search
  • radius: Search radius in kilometers
  • dateFrom/dateTo: Filter by posting date range

Security Implementation

Input Validation & Sanitization

Zod Schema Validation - All incoming requests are validated against TypeScript-first schemas before reaching controllers. This ensures type safety and catches malformed data early in the request lifecycle.

// Example: Post creation schemaconstcreatePostSchema=z.object({title: z.string().min(3).max(100),description: z.string().min(10).max(2000),category: z.enum(['pet','electronics','documents','jewelry','other']),type: z.enum(['lost','found']),location: z.object({lat: z.number().min(43.5).max(48.3),lon: z.number().min(20.2).max(29.7),display_name: z.string()})});

NoSQL Injection Prevention - express-mongo-sanitize middleware strips out $ and . characters from user input, preventing MongoDB operator injection attacks. This protects against malicious queries that attempt to manipulate database operations.

// Sanitization applied globally to all routesapp.use(mongoSanitize());// Example attack prevented:// { "email": { "$gt": "" }} → { "email": "" }

Rate Limiting Architecture

Redis-backed rate limiting prevents abuse and ensures fair resource allocation. Different endpoints have tiered limits based on their resource intensity:

Endpoint TypeWindowLimitRationale
Registration10 min5Prevent bot account creation
Login5 min10Balance security vs. user experience
Post Creation10 min93Allow legitimate use while preventing spam
Image Upload5 min115Protect storage and bandwidth
Geocoding1 min60Respect external API fair use
Comments1 min5Prevent spam without hindering discussion
Profile Updates1 min2Critical operations need strict limits

Rate limit state is stored in Redis with key prefixes (rl_register:, rl_login:, etc.) for namespace isolation. The system returns standardized error responses with retry-after headers compliant with RFC 6585.

Authentication & Authorization

JWT Token Strategy:

  • Access Tokens: Short-lived (15 minutes), contain user ID and role
  • Refresh Tokens: Long-lived (7 days), stored in httpOnly cookies
  • Token Rotation: Each refresh generates new token pair, old tokens invalidated
  • Signature Algorithm: HS256 with secrets ≥32 characters

Cookie Security:

res.cookie('refreshToken',token,{httpOnly: true,// Prevent XSS accesssecure: true,// HTTPS only in productionsameSite: 'strict',// CSRF protectionmaxAge: 7*24*60*60*1000// 7 days});

Password Security:

  • bcrypt hashing with salt rounds = 12
  • Minimum 8 characters with complexity requirements
  • Passwords never logged or returned in responses
  • Secure password reset with time-limited tokens

HTTP Security Headers (Helmet.js)

app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],imgSrc: ["'self'","data:","https://res.cloudinary.com"],scriptSrc: ["'self'","'unsafe-inline'"],// Next.js requirement}},hsts: {maxAge: 31536000,includeSubDomains: true,preload: true}}));

Enabled protections include CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

File Upload Security

Multer Configuration:

  • Memory storage (no disk writes in development)
  • MIME type validation before processing
  • Size limits: 5MB per file, max 5 files per request
  • Allowed formats: JPEG, JPG, PNG, WebP only
  • Error handling for malformed uploads

Cloudinary Integration:

  • Automatic format optimization (WebP conversion)
  • Lazy transformation for responsive images
  • Signed upload URLs prevent unauthorized uploads
  • CDN delivery reduces origin server load

CORS Policy

app.use(cors({origin: process.env.FRONTEND_URL,// Whitelist specific origincredentials: true,// Allow cookiesmethods: ['GET','POST','PUT','PATCH','DELETE'],allowedHeaders: ['Content-Type','Authorization']}));

Strict CORS configuration prevents cross-origin attacks while enabling authenticated requests from the frontend.

Performance Optimizations

Caching Strategy

Redis Caching Layer:

  • Geocoding responses: 1-hour TTL (key: search:{query}:{limit})
  • Reverse geocoding: 1-hour TTL (key: reverse:{lat}:{lon})
  • Rate limit counters: Sliding window with automatic expiration
  • Session tokens: TTL matches JWT expiration

Cache hit rate monitoring shows ~75% cache hits for geocoding queries, reducing external API calls and improving response times from ~800ms to ~15ms.

Database Indexing

MongoDB Indexes:

// Geospatial index for location queriespostSchema.index({location: '2dsphere'});// Compound index for filtered searchespostSchema.index({category: 1,type: 1,createdAt: -1});// Text index for full-text searchpostSchema.index({title: 'text',description: 'text'});// User lookup optimizationpostSchema.index({userId: 1,status: 1});

Query performance benchmarks show 95th percentile latency under 50ms for indexed queries vs. 2000ms+ for full collection scans.

Image Optimization Pipeline

Cloudinary Transformations:

  • Automatic WebP conversion with fallback to original format
  • Responsive image variants (thumbnail, medium, full)
  • Lazy loading with low-quality image placeholders (LQIP)
  • CDN edge caching for global delivery

Optimization Results:

  • Average image size: 2.3MB → 180KB (WebP)
  • Page load time: 4.2s → 1.8s
  • Bandwidth savings: ~92%

Frontend Optimizations

Next.js Features:

  • Automatic code splitting per route
  • Server-side rendering for SEO and initial load performance
  • Static generation for public pages
  • Image component with built-in lazy loading
  • Font optimization with Geist preloading

Bundle Analysis:

  • Initial JS bundle: 142KB gzipped
  • First Contentful Paint: ~1.2s
  • Time to Interactive: ~2.3s
  • Lighthouse Performance Score: 94/100

Technical Challenges

Geospatial Accuracy & Validation

Challenge: Ensuring coordinates are valid and fall within Romania's boundaries while handling edge cases like users near borders or coordinates from external sources.

Solution: Implemented strict Zod validation with min/max constraints on latitude (43.5-48.3°N) and longitude (20.2-29.7°E). Added fallback mechanisms when Nominatim API fails—system gracefully degrades to displaying raw coordinates rather than throwing errors.

constreverseSchema=z.object({lat: z.coerce.number().min(43.5).max(48.3),lon: z.coerce.number().min(20.2).max(29.7)});// Fallback response on API failurecatch(error){res.json({display_name: `${lat.toFixed(5)}, ${lon.toFixed(5)}`,address: {},
lat, lon
});}

Concurrent Update Conflicts

Challenge: Race conditions when multiple users interact with the same post simultaneously (editing, commenting, marking resolved).

Solution: Leveraged MongoDB's atomic update operators ($set, $push, $inc) and implemented optimistic locking with version fields. Critical operations use transactions to ensure data consistency.

// Atomic operation prevents race conditionsawaitPost.findByIdAndUpdate(postId,{$set: {status: 'solved',solvedAt: newDate()}},{new: true,runValidators: true});

External API Resilience

Challenge: Nominatim API rate limits (1 request/second) and occasional timeouts causing user-facing errors.

Solution: Three-layered approach:

  1. Redis caching with 1-hour TTL reduces API calls by ~75%
  2. Timeout configuration (5s) prevents hanging requests
  3. Graceful degradation returns partial data instead of failing

Rate limiting on the geocoding endpoint (60/min) ensures compliance with Nominatim's usage policy while accommodating legitimate user activity.

Scalability & Resource Management

Challenge: As user base grows, managing database connections, Redis connections, and memory usage becomes critical.

Solution:

  • MongoDB connection pooling (min: 10, max: 50 connections)
  • Redis connection reuse with single client instance
  • Image uploads limited to 5MB to prevent memory exhaustion
  • Rate limiting prevents resource starvation from malicious actors
  • Horizontal scaling strategy with load balancer-ready stateless design

Search Performance at Scale

Challenge: Text search across thousands of posts with multiple filters (location, category, date) must remain fast.

Solution: Implemented compound indexes covering common query patterns and MongoDB aggregation pipeline for complex searches. Future optimization plan includes Elasticsearch integration for full-text search once post volume exceeds 100K records.

Mobile Responsive Design

Mobile HomepageMobile PostMobile Map

Fully responsive design with touch-optimized map controls, collapsible filters, and mobile-first form layouts. CSS Grid and Flexbox ensure consistent layouts across devices. Breakpoints at 768px and 1024px accommodate tablets and desktops.

Installation

Prerequisites

  • Node.js 18+ and npm
  • MongoDB 5.0+
  • Redis 6.0+
  • Cloudinary account (free tier sufficient)

Setup Instructions

# Clone repository
git clone https://github.com/Rotis-Web/lostfound.git
cd lostfound
# Install frontend dependenciescd client
npm install
# Install backend dependenciescd ../server
npm install
# Start MongoDB and Redis (if running locally)# macOS with Homebrew:
brew services start mongodb-community
brew services start redis
# Run development servers
npm run dev:all
# This starts both frontend (port 3000) and backend (port 8000)

Environment Configuration

Frontend Configuration

Create client/.env.local:

NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name

Backend Configuration

Create server/.env:

# Server
PORT=8000
NODE_ENV=development
# Database
MONGO_URI=mongodb://localhost:27017/lostfound
# Production: mongodb+srv://username:password@cluster.mongodb.net/lostfound# Redis
REDIS_URL=redis://localhost:6379
# Production: redis://username:password@host:port# Application URLs
APP_ORIGIN=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# JWT Configuration (generate random 32+ char strings)
JWT_SECRET=your_secure_secret_min_32_chars_use_openssl_rand
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret_different_from_above
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary (sign up at cloudinary.com)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_specific_password

Generating Secure Secrets:

# Use OpenSSL to generate random secrets
openssl rand -base64 32

Design Philosophy

The interface prioritizes clarity and accessibility with a warm color palette that conveys hope and urgency. The design system balances vibrant accent colors with professional neutrals to create an approachable yet trustworthy aesthetic.

Color Palette

ColorHexUsage
Yellow Primary#ffd700CTAs, highlights - conveys energy and optimism
Dark Blue#2c3e60Headers, text - inspires trust and professionalism
Orange Accent#f57a4eImportant buttons, alerts - draws attention
Green Success#51e188Success messages, resolved posts
Red Alert#ff4444Error states, urgent actions
Neutral Gray#9ca3afSecondary text, borders, disabled states

Typography

  • Font Family: Geist Sans - Modern, highly legible sans-serif optimized for UI
  • Heading Scale: 2.5rem / 2rem / 1.5rem / 1.25rem / 1rem
  • Body Text: 1rem (16px) with 1.5 line height for optimal readability
  • Code/Monospace: Geist Mono for technical content

Built to reunite people with what matters most

TypeScriptNext.jsExpressMongoDBRedis

License: MIT | Developer: Alexandru Rotar

About

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

194 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🔍 Lost & Found - Reuniting People with Lost Items & Pets

Lost & Found Banner

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching. The platform combines real-time mapping, advanced search algorithms, and secure user authentication to create a comprehensive lost-and-found ecosystem.

Table of Contents

Project Overview

This application addresses the challenge of efficiently connecting people who have lost items with those who have found them. By leveraging geospatial indexing and caching strategies, the platform delivers fast, location-aware search results while maintaining data security and system reliability.

Key Technical Highlights:

  • Geospatial queries with MongoDB 2dsphere indexes
  • Redis-backed rate limiting and caching
  • JWT-based authentication with refresh token rotation
  • Automated image optimization via Cloudinary CDN
  • XSS protection through input sanitization with express-mongo-sanitize
  • Comprehensive input validation using Zod schemas

Preview

Post PageCreate Post
DashboardHomepage

Core Features

Geospatial Posting & Discovery

Users can create posts with precise coordinates using Leaflet.js integration and OpenStreetMap's Nominatim API. The system implements MongoDB geospatial indexes for efficient radius-based queries, enabling users to discover nearby lost/found items within customizable distance ranges.

Advanced Search & Filtering

Multi-parameter search functionality includes text matching, date ranges, categories, and location-based filtering. Search queries are optimized through Redis caching with intelligent TTL management (1-hour expiration), reducing external API calls and improving response times.

Printable Flyer Generator

Automated generation of professional PDF flyers with QR codes linking back to the online post. Templates are optimized for A4 printing and include customizable layouts that adapt to different item types.

User Management System

Secure authentication flow with JWT tokens (access + refresh), email verification, and password recovery. User profiles maintain posting history with dashboard analytics for tracking active and resolved posts. Users can bookmark posts for later reference and manage their saved collections.

Comment System

Real-time commenting functionality allows users to ask questions, provide updates, or coordinate meetups directly on posts. Comments are rate-limited to prevent spam and support threaded discussions.

Technical Architecture

Technology Stack

Next.jsTypeScriptExpress.jsReactMongoDBRedisCloudinaryLeafletSASSContext-API

Frontend:

  • Next.js 14 with App Router
  • TypeScript for type safety
  • SCSS Modules for component-scoped styling
  • Leaflet.js for interactive maps
  • React Context API for state management

Backend:

  • Express.js with TypeScript
  • Mongoose ODM for MongoDB interactions
  • Redis for session storage and caching
  • Helmet.js for security headers
  • Morgan for request logging
  • express-mongo-sanitize for NoSQL injection prevention
  • Zod for schema validation

Infrastructure:

  • MongoDB Atlas for database hosting
  • Redis Cloud for caching layer
  • Cloudinary for image CDN
  • Vercel for frontend deployment
  • Railway/Render for backend deployment

API Documentation

Base URL

Production: https://api.lostfound.ro/api/v1
Development: http://localhost:8000/api/v1

Authentication Routes (/auth)

MethodEndpointRate LimitDescription
POST/register5/10minCreate new user account with email verification
POST/login10/5minAuthenticate user and issue JWT tokens
POST/logout-Invalidate refresh token and clear cookies
POST/refresh-token-Generate new access token using refresh token
POST/verify-email10/minConfirm email address with verification code
POST/forgot-password10/minRequest password reset email
POST/reset-password10/minReset password using token from email

Authentication Flow:

  1. User registers → Email verification sent
  2. User verifies email → Account activated
  3. User logs in → Access token (15min) + Refresh token (7d) issued
  4. Access token expires → Client requests new token using refresh token
  5. Refresh token expires → User must log in again

Post Management Routes (/post)

MethodEndpointAuthRate LimitDescription
POST/create93/10minCreate new lost/found post with images
GET/:postId-30/minRetrieve single post by ID
PUT/edit/:postId20/5minUpdate post details and images
PATCH/solve/:postId30/minMark post as resolved
DELETE/delete/:postId10/5minDelete user's own post
GET/user-posts30/minGet all posts by authenticated user
GET/latest-30/minFetch recent posts with pagination

Post Creation Example:

POST/api/v1/post/createContent-Type: multipart/form-data
Authorization: Bearer{access_token}{title: "Lost Black Labrador",description: "Last seen near Central Park",category: "pet",type: "lost",location: {lat: 44.4268,lon: 26.1025,display_name: "Bucharest, Romania"},contactInfo: {phone: "+40123456789",email: "contact@example.com"},images: [File,File]// Max 5 images, 5MB each}

User Management Routes (/user)

MethodEndpointAuthRate LimitDescription
GET/profile30/minGet authenticated user's profile
GET/public-profile/:id-30/minView public user profile
PUT/change-password2/minUpdate user password
PUT/change-profile-image2/minUpload new profile picture
DELETE/delete-account2/minPermanently delete user account
GET/saved-posts-Retrieve user's bookmarked posts
POST/save-post30/minBookmark a post
POST/remove-post30/minRemove post from bookmarks

Geocoding Routes (/geo)

MethodEndpointRate LimitDescription
GET/search?q={query}&limit={n}60/minForward geocoding (address → coordinates)
GET/reverse?lat={lat}&lon={lon}60/minReverse geocoding (coordinates → address)
GET/health-Service health check

Geocoding Features:

  • Results cached in Redis for 1 hour
  • Country-specific to Romania (countrycodes=ro)
  • Coordinate validation: lat ∈ [43.5, 48.3], lon ∈ [20.2, 29.7]
  • Automatic language localization (Romanian)
  • Deduplicated results with importance scoring

Comment Routes (/comment)

MethodEndpointAuthRate LimitDescription
POST/create5/minAdd comment to post
DELETE/delete/:commentId5/minDelete own comment

Search Routes (/search)

MethodEndpointDescription
GET/posts?q={query}&category={cat}&location={loc}&radius={km}&dateFrom={date}&dateTo={date}Advanced post search

Search Parameters:

  • q: Text search in title/description
  • category: Filter by category (pet, electronics, documents, etc.)
  • location: Center point for radius search
  • radius: Search radius in kilometers
  • dateFrom/dateTo: Filter by posting date range

Security Implementation

Input Validation & Sanitization

Zod Schema Validation - All incoming requests are validated against TypeScript-first schemas before reaching controllers. This ensures type safety and catches malformed data early in the request lifecycle.

// Example: Post creation schemaconstcreatePostSchema=z.object({title: z.string().min(3).max(100),description: z.string().min(10).max(2000),category: z.enum(['pet','electronics','documents','jewelry','other']),type: z.enum(['lost','found']),location: z.object({lat: z.number().min(43.5).max(48.3),lon: z.number().min(20.2).max(29.7),display_name: z.string()})});

NoSQL Injection Prevention - express-mongo-sanitize middleware strips out $ and . characters from user input, preventing MongoDB operator injection attacks. This protects against malicious queries that attempt to manipulate database operations.

// Sanitization applied globally to all routesapp.use(mongoSanitize());// Example attack prevented:// { "email": { "$gt": "" }} → { "email": "" }

Rate Limiting Architecture

Redis-backed rate limiting prevents abuse and ensures fair resource allocation. Different endpoints have tiered limits based on their resource intensity:

Endpoint TypeWindowLimitRationale
Registration10 min5Prevent bot account creation
Login5 min10Balance security vs. user experience
Post Creation10 min93Allow legitimate use while preventing spam
Image Upload5 min115Protect storage and bandwidth
Geocoding1 min60Respect external API fair use
Comments1 min5Prevent spam without hindering discussion
Profile Updates1 min2Critical operations need strict limits

Rate limit state is stored in Redis with key prefixes (rl_register:, rl_login:, etc.) for namespace isolation. The system returns standardized error responses with retry-after headers compliant with RFC 6585.

Authentication & Authorization

JWT Token Strategy:

  • Access Tokens: Short-lived (15 minutes), contain user ID and role
  • Refresh Tokens: Long-lived (7 days), stored in httpOnly cookies
  • Token Rotation: Each refresh generates new token pair, old tokens invalidated
  • Signature Algorithm: HS256 with secrets ≥32 characters

Cookie Security:

res.cookie('refreshToken',token,{httpOnly: true,// Prevent XSS accesssecure: true,// HTTPS only in productionsameSite: 'strict',// CSRF protectionmaxAge: 7*24*60*60*1000// 7 days});

Password Security:

  • bcrypt hashing with salt rounds = 12
  • Minimum 8 characters with complexity requirements
  • Passwords never logged or returned in responses
  • Secure password reset with time-limited tokens

HTTP Security Headers (Helmet.js)

app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],imgSrc: ["'self'","data:","https://res.cloudinary.com"],scriptSrc: ["'self'","'unsafe-inline'"],// Next.js requirement}},hsts: {maxAge: 31536000,includeSubDomains: true,preload: true}}));

Enabled protections include CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

File Upload Security

Multer Configuration:

  • Memory storage (no disk writes in development)
  • MIME type validation before processing
  • Size limits: 5MB per file, max 5 files per request
  • Allowed formats: JPEG, JPG, PNG, WebP only
  • Error handling for malformed uploads

Cloudinary Integration:

  • Automatic format optimization (WebP conversion)
  • Lazy transformation for responsive images
  • Signed upload URLs prevent unauthorized uploads
  • CDN delivery reduces origin server load

CORS Policy

app.use(cors({origin: process.env.FRONTEND_URL,// Whitelist specific origincredentials: true,// Allow cookiesmethods: ['GET','POST','PUT','PATCH','DELETE'],allowedHeaders: ['Content-Type','Authorization']}));

Strict CORS configuration prevents cross-origin attacks while enabling authenticated requests from the frontend.

Performance Optimizations

Caching Strategy

Redis Caching Layer:

  • Geocoding responses: 1-hour TTL (key: search:{query}:{limit})
  • Reverse geocoding: 1-hour TTL (key: reverse:{lat}:{lon})
  • Rate limit counters: Sliding window with automatic expiration
  • Session tokens: TTL matches JWT expiration

Cache hit rate monitoring shows ~75% cache hits for geocoding queries, reducing external API calls and improving response times from ~800ms to ~15ms.

Database Indexing

MongoDB Indexes:

// Geospatial index for location queriespostSchema.index({location: '2dsphere'});// Compound index for filtered searchespostSchema.index({category: 1,type: 1,createdAt: -1});// Text index for full-text searchpostSchema.index({title: 'text',description: 'text'});// User lookup optimizationpostSchema.index({userId: 1,status: 1});

Query performance benchmarks show 95th percentile latency under 50ms for indexed queries vs. 2000ms+ for full collection scans.

Image Optimization Pipeline

Cloudinary Transformations:

  • Automatic WebP conversion with fallback to original format
  • Responsive image variants (thumbnail, medium, full)
  • Lazy loading with low-quality image placeholders (LQIP)
  • CDN edge caching for global delivery

Optimization Results:

  • Average image size: 2.3MB → 180KB (WebP)
  • Page load time: 4.2s → 1.8s
  • Bandwidth savings: ~92%

Frontend Optimizations

Next.js Features:

  • Automatic code splitting per route
  • Server-side rendering for SEO and initial load performance
  • Static generation for public pages
  • Image component with built-in lazy loading
  • Font optimization with Geist preloading

Bundle Analysis:

  • Initial JS bundle: 142KB gzipped
  • First Contentful Paint: ~1.2s
  • Time to Interactive: ~2.3s
  • Lighthouse Performance Score: 94/100

Technical Challenges

Geospatial Accuracy & Validation

Challenge: Ensuring coordinates are valid and fall within Romania's boundaries while handling edge cases like users near borders or coordinates from external sources.

Solution: Implemented strict Zod validation with min/max constraints on latitude (43.5-48.3°N) and longitude (20.2-29.7°E). Added fallback mechanisms when Nominatim API fails—system gracefully degrades to displaying raw coordinates rather than throwing errors.

constreverseSchema=z.object({lat: z.coerce.number().min(43.5).max(48.3),lon: z.coerce.number().min(20.2).max(29.7)});// Fallback response on API failurecatch(error){res.json({display_name: `${lat.toFixed(5)}, ${lon.toFixed(5)}`,address: {},
lat, lon
});}

Concurrent Update Conflicts

Challenge: Race conditions when multiple users interact with the same post simultaneously (editing, commenting, marking resolved).

Solution: Leveraged MongoDB's atomic update operators ($set, $push, $inc) and implemented optimistic locking with version fields. Critical operations use transactions to ensure data consistency.

// Atomic operation prevents race conditionsawaitPost.findByIdAndUpdate(postId,{$set: {status: 'solved',solvedAt: newDate()}},{new: true,runValidators: true});

External API Resilience

Challenge: Nominatim API rate limits (1 request/second) and occasional timeouts causing user-facing errors.

Solution: Three-layered approach:

  1. Redis caching with 1-hour TTL reduces API calls by ~75%
  2. Timeout configuration (5s) prevents hanging requests
  3. Graceful degradation returns partial data instead of failing

Rate limiting on the geocoding endpoint (60/min) ensures compliance with Nominatim's usage policy while accommodating legitimate user activity.

Scalability & Resource Management

Challenge: As user base grows, managing database connections, Redis connections, and memory usage becomes critical.

Solution:

  • MongoDB connection pooling (min: 10, max: 50 connections)
  • Redis connection reuse with single client instance
  • Image uploads limited to 5MB to prevent memory exhaustion
  • Rate limiting prevents resource starvation from malicious actors
  • Horizontal scaling strategy with load balancer-ready stateless design

Search Performance at Scale

Challenge: Text search across thousands of posts with multiple filters (location, category, date) must remain fast.

Solution: Implemented compound indexes covering common query patterns and MongoDB aggregation pipeline for complex searches. Future optimization plan includes Elasticsearch integration for full-text search once post volume exceeds 100K records.

Mobile Responsive Design

Mobile HomepageMobile PostMobile Map

Fully responsive design with touch-optimized map controls, collapsible filters, and mobile-first form layouts. CSS Grid and Flexbox ensure consistent layouts across devices. Breakpoints at 768px and 1024px accommodate tablets and desktops.

Installation

Prerequisites

  • Node.js 18+ and npm
  • MongoDB 5.0+
  • Redis 6.0+
  • Cloudinary account (free tier sufficient)

Setup Instructions

# Clone repository
git clone https://github.com/Rotis-Web/lostfound.git
cd lostfound
# Install frontend dependenciescd client
npm install
# Install backend dependenciescd ../server
npm install
# Start MongoDB and Redis (if running locally)# macOS with Homebrew:
brew services start mongodb-community
brew services start redis
# Run development servers
npm run dev:all
# This starts both frontend (port 3000) and backend (port 8000)

Environment Configuration

Frontend Configuration

Create client/.env.local:

NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name

Backend Configuration

Create server/.env:

# Server
PORT=8000
NODE_ENV=development
# Database
MONGO_URI=mongodb://localhost:27017/lostfound
# Production: mongodb+srv://username:password@cluster.mongodb.net/lostfound# Redis
REDIS_URL=redis://localhost:6379
# Production: redis://username:password@host:port# Application URLs
APP_ORIGIN=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# JWT Configuration (generate random 32+ char strings)
JWT_SECRET=your_secure_secret_min_32_chars_use_openssl_rand
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret_different_from_above
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary (sign up at cloudinary.com)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_specific_password

Generating Secure Secrets:

# Use OpenSSL to generate random secrets
openssl rand -base64 32

Design Philosophy

The interface prioritizes clarity and accessibility with a warm color palette that conveys hope and urgency. The design system balances vibrant accent colors with professional neutrals to create an approachable yet trustworthy aesthetic.

Color Palette

ColorHexUsage
Yellow Primary#ffd700CTAs, highlights - conveys energy and optimism
Dark Blue#2c3e60Headers, text - inspires trust and professionalism
Orange Accent#f57a4eImportant buttons, alerts - draws attention
Green Success#51e188Success messages, resolved posts
Red Alert#ff4444Error states, urgent actions
Neutral Gray#9ca3afSecondary text, borders, disabled states

Typography

  • Font Family: Geist Sans - Modern, highly legible sans-serif optimized for UI
  • Heading Scale: 2.5rem / 2rem / 1.5rem / 1.25rem / 1rem
  • Body Text: 1rem (16px) with 1.5 line height for optimal readability
  • Code/Monospace: Geist Mono for technical content

Built to reunite people with what matters most

TypeScriptNext.jsExpressMongoDBRedis

License: MIT | Developer: Alexandru Rotar

About

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

194 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🔍 Lost & Found - Reuniting People with Lost Items & Pets

Lost & Found Banner

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching. The platform combines real-time mapping, advanced search algorithms, and secure user authentication to create a comprehensive lost-and-found ecosystem.

Table of Contents

Project Overview

This application addresses the challenge of efficiently connecting people who have lost items with those who have found them. By leveraging geospatial indexing and caching strategies, the platform delivers fast, location-aware search results while maintaining data security and system reliability.

Key Technical Highlights:

  • Geospatial queries with MongoDB 2dsphere indexes
  • Redis-backed rate limiting and caching
  • JWT-based authentication with refresh token rotation
  • Automated image optimization via Cloudinary CDN
  • XSS protection through input sanitization with express-mongo-sanitize
  • Comprehensive input validation using Zod schemas

Preview

Post PageCreate Post
DashboardHomepage

Core Features

Geospatial Posting & Discovery

Users can create posts with precise coordinates using Leaflet.js integration and OpenStreetMap's Nominatim API. The system implements MongoDB geospatial indexes for efficient radius-based queries, enabling users to discover nearby lost/found items within customizable distance ranges.

Advanced Search & Filtering

Multi-parameter search functionality includes text matching, date ranges, categories, and location-based filtering. Search queries are optimized through Redis caching with intelligent TTL management (1-hour expiration), reducing external API calls and improving response times.

Printable Flyer Generator

Automated generation of professional PDF flyers with QR codes linking back to the online post. Templates are optimized for A4 printing and include customizable layouts that adapt to different item types.

User Management System

Secure authentication flow with JWT tokens (access + refresh), email verification, and password recovery. User profiles maintain posting history with dashboard analytics for tracking active and resolved posts. Users can bookmark posts for later reference and manage their saved collections.

Comment System

Real-time commenting functionality allows users to ask questions, provide updates, or coordinate meetups directly on posts. Comments are rate-limited to prevent spam and support threaded discussions.

Technical Architecture

Technology Stack

Next.jsTypeScriptExpress.jsReactMongoDBRedisCloudinaryLeafletSASSContext-API

Frontend:

  • Next.js 14 with App Router
  • TypeScript for type safety
  • SCSS Modules for component-scoped styling
  • Leaflet.js for interactive maps
  • React Context API for state management

Backend:

  • Express.js with TypeScript
  • Mongoose ODM for MongoDB interactions
  • Redis for session storage and caching
  • Helmet.js for security headers
  • Morgan for request logging
  • express-mongo-sanitize for NoSQL injection prevention
  • Zod for schema validation

Infrastructure:

  • MongoDB Atlas for database hosting
  • Redis Cloud for caching layer
  • Cloudinary for image CDN
  • Vercel for frontend deployment
  • Railway/Render for backend deployment

API Documentation

Base URL

Production: https://api.lostfound.ro/api/v1
Development: http://localhost:8000/api/v1

Authentication Routes (/auth)

MethodEndpointRate LimitDescription
POST/register5/10minCreate new user account with email verification
POST/login10/5minAuthenticate user and issue JWT tokens
POST/logout-Invalidate refresh token and clear cookies
POST/refresh-token-Generate new access token using refresh token
POST/verify-email10/minConfirm email address with verification code
POST/forgot-password10/minRequest password reset email
POST/reset-password10/minReset password using token from email

Authentication Flow:

  1. User registers → Email verification sent
  2. User verifies email → Account activated
  3. User logs in → Access token (15min) + Refresh token (7d) issued
  4. Access token expires → Client requests new token using refresh token
  5. Refresh token expires → User must log in again

Post Management Routes (/post)

MethodEndpointAuthRate LimitDescription
POST/create93/10minCreate new lost/found post with images
GET/:postId-30/minRetrieve single post by ID
PUT/edit/:postId20/5minUpdate post details and images
PATCH/solve/:postId30/minMark post as resolved
DELETE/delete/:postId10/5minDelete user's own post
GET/user-posts30/minGet all posts by authenticated user
GET/latest-30/minFetch recent posts with pagination

Post Creation Example:

POST/api/v1/post/createContent-Type: multipart/form-data
Authorization: Bearer{access_token}{title: "Lost Black Labrador",description: "Last seen near Central Park",category: "pet",type: "lost",location: {lat: 44.4268,lon: 26.1025,display_name: "Bucharest, Romania"},contactInfo: {phone: "+40123456789",email: "contact@example.com"},images: [File,File]// Max 5 images, 5MB each}

User Management Routes (/user)

MethodEndpointAuthRate LimitDescription
GET/profile30/minGet authenticated user's profile
GET/public-profile/:id-30/minView public user profile
PUT/change-password2/minUpdate user password
PUT/change-profile-image2/minUpload new profile picture
DELETE/delete-account2/minPermanently delete user account
GET/saved-posts-Retrieve user's bookmarked posts
POST/save-post30/minBookmark a post
POST/remove-post30/minRemove post from bookmarks

Geocoding Routes (/geo)

MethodEndpointRate LimitDescription
GET/search?q={query}&limit={n}60/minForward geocoding (address → coordinates)
GET/reverse?lat={lat}&lon={lon}60/minReverse geocoding (coordinates → address)
GET/health-Service health check

Geocoding Features:

  • Results cached in Redis for 1 hour
  • Country-specific to Romania (countrycodes=ro)
  • Coordinate validation: lat ∈ [43.5, 48.3], lon ∈ [20.2, 29.7]
  • Automatic language localization (Romanian)
  • Deduplicated results with importance scoring

Comment Routes (/comment)

MethodEndpointAuthRate LimitDescription
POST/create5/minAdd comment to post
DELETE/delete/:commentId5/minDelete own comment

Search Routes (/search)

MethodEndpointDescription
GET/posts?q={query}&category={cat}&location={loc}&radius={km}&dateFrom={date}&dateTo={date}Advanced post search

Search Parameters:

  • q: Text search in title/description
  • category: Filter by category (pet, electronics, documents, etc.)
  • location: Center point for radius search
  • radius: Search radius in kilometers
  • dateFrom/dateTo: Filter by posting date range

Security Implementation

Input Validation & Sanitization

Zod Schema Validation - All incoming requests are validated against TypeScript-first schemas before reaching controllers. This ensures type safety and catches malformed data early in the request lifecycle.

// Example: Post creation schemaconstcreatePostSchema=z.object({title: z.string().min(3).max(100),description: z.string().min(10).max(2000),category: z.enum(['pet','electronics','documents','jewelry','other']),type: z.enum(['lost','found']),location: z.object({lat: z.number().min(43.5).max(48.3),lon: z.number().min(20.2).max(29.7),display_name: z.string()})});

NoSQL Injection Prevention - express-mongo-sanitize middleware strips out $ and . characters from user input, preventing MongoDB operator injection attacks. This protects against malicious queries that attempt to manipulate database operations.

// Sanitization applied globally to all routesapp.use(mongoSanitize());// Example attack prevented:// { "email": { "$gt": "" }} → { "email": "" }

Rate Limiting Architecture

Redis-backed rate limiting prevents abuse and ensures fair resource allocation. Different endpoints have tiered limits based on their resource intensity:

Endpoint TypeWindowLimitRationale
Registration10 min5Prevent bot account creation
Login5 min10Balance security vs. user experience
Post Creation10 min93Allow legitimate use while preventing spam
Image Upload5 min115Protect storage and bandwidth
Geocoding1 min60Respect external API fair use
Comments1 min5Prevent spam without hindering discussion
Profile Updates1 min2Critical operations need strict limits

Rate limit state is stored in Redis with key prefixes (rl_register:, rl_login:, etc.) for namespace isolation. The system returns standardized error responses with retry-after headers compliant with RFC 6585.

Authentication & Authorization

JWT Token Strategy:

  • Access Tokens: Short-lived (15 minutes), contain user ID and role
  • Refresh Tokens: Long-lived (7 days), stored in httpOnly cookies
  • Token Rotation: Each refresh generates new token pair, old tokens invalidated
  • Signature Algorithm: HS256 with secrets ≥32 characters

Cookie Security:

res.cookie('refreshToken',token,{httpOnly: true,// Prevent XSS accesssecure: true,// HTTPS only in productionsameSite: 'strict',// CSRF protectionmaxAge: 7*24*60*60*1000// 7 days});

Password Security:

  • bcrypt hashing with salt rounds = 12
  • Minimum 8 characters with complexity requirements
  • Passwords never logged or returned in responses
  • Secure password reset with time-limited tokens

HTTP Security Headers (Helmet.js)

app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],imgSrc: ["'self'","data:","https://res.cloudinary.com"],scriptSrc: ["'self'","'unsafe-inline'"],// Next.js requirement}},hsts: {maxAge: 31536000,includeSubDomains: true,preload: true}}));

Enabled protections include CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

File Upload Security

Multer Configuration:

  • Memory storage (no disk writes in development)
  • MIME type validation before processing
  • Size limits: 5MB per file, max 5 files per request
  • Allowed formats: JPEG, JPG, PNG, WebP only
  • Error handling for malformed uploads

Cloudinary Integration:

  • Automatic format optimization (WebP conversion)
  • Lazy transformation for responsive images
  • Signed upload URLs prevent unauthorized uploads
  • CDN delivery reduces origin server load

CORS Policy

app.use(cors({origin: process.env.FRONTEND_URL,// Whitelist specific origincredentials: true,// Allow cookiesmethods: ['GET','POST','PUT','PATCH','DELETE'],allowedHeaders: ['Content-Type','Authorization']}));

Strict CORS configuration prevents cross-origin attacks while enabling authenticated requests from the frontend.

Performance Optimizations

Caching Strategy

Redis Caching Layer:

  • Geocoding responses: 1-hour TTL (key: search:{query}:{limit})
  • Reverse geocoding: 1-hour TTL (key: reverse:{lat}:{lon})
  • Rate limit counters: Sliding window with automatic expiration
  • Session tokens: TTL matches JWT expiration

Cache hit rate monitoring shows ~75% cache hits for geocoding queries, reducing external API calls and improving response times from ~800ms to ~15ms.

Database Indexing

MongoDB Indexes:

// Geospatial index for location queriespostSchema.index({location: '2dsphere'});// Compound index for filtered searchespostSchema.index({category: 1,type: 1,createdAt: -1});// Text index for full-text searchpostSchema.index({title: 'text',description: 'text'});// User lookup optimizationpostSchema.index({userId: 1,status: 1});

Query performance benchmarks show 95th percentile latency under 50ms for indexed queries vs. 2000ms+ for full collection scans.

Image Optimization Pipeline

Cloudinary Transformations:

  • Automatic WebP conversion with fallback to original format
  • Responsive image variants (thumbnail, medium, full)
  • Lazy loading with low-quality image placeholders (LQIP)
  • CDN edge caching for global delivery

Optimization Results:

  • Average image size: 2.3MB → 180KB (WebP)
  • Page load time: 4.2s → 1.8s
  • Bandwidth savings: ~92%

Frontend Optimizations

Next.js Features:

  • Automatic code splitting per route
  • Server-side rendering for SEO and initial load performance
  • Static generation for public pages
  • Image component with built-in lazy loading
  • Font optimization with Geist preloading

Bundle Analysis:

  • Initial JS bundle: 142KB gzipped
  • First Contentful Paint: ~1.2s
  • Time to Interactive: ~2.3s
  • Lighthouse Performance Score: 94/100

Technical Challenges

Geospatial Accuracy & Validation

Challenge: Ensuring coordinates are valid and fall within Romania's boundaries while handling edge cases like users near borders or coordinates from external sources.

Solution: Implemented strict Zod validation with min/max constraints on latitude (43.5-48.3°N) and longitude (20.2-29.7°E). Added fallback mechanisms when Nominatim API fails—system gracefully degrades to displaying raw coordinates rather than throwing errors.

constreverseSchema=z.object({lat: z.coerce.number().min(43.5).max(48.3),lon: z.coerce.number().min(20.2).max(29.7)});// Fallback response on API failurecatch(error){res.json({display_name: `${lat.toFixed(5)}, ${lon.toFixed(5)}`,address: {},
lat, lon
});}

Concurrent Update Conflicts

Challenge: Race conditions when multiple users interact with the same post simultaneously (editing, commenting, marking resolved).

Solution: Leveraged MongoDB's atomic update operators ($set, $push, $inc) and implemented optimistic locking with version fields. Critical operations use transactions to ensure data consistency.

// Atomic operation prevents race conditionsawaitPost.findByIdAndUpdate(postId,{$set: {status: 'solved',solvedAt: newDate()}},{new: true,runValidators: true});

External API Resilience

Challenge: Nominatim API rate limits (1 request/second) and occasional timeouts causing user-facing errors.

Solution: Three-layered approach:

  1. Redis caching with 1-hour TTL reduces API calls by ~75%
  2. Timeout configuration (5s) prevents hanging requests
  3. Graceful degradation returns partial data instead of failing

Rate limiting on the geocoding endpoint (60/min) ensures compliance with Nominatim's usage policy while accommodating legitimate user activity.

Scalability & Resource Management

Challenge: As user base grows, managing database connections, Redis connections, and memory usage becomes critical.

Solution:

  • MongoDB connection pooling (min: 10, max: 50 connections)
  • Redis connection reuse with single client instance
  • Image uploads limited to 5MB to prevent memory exhaustion
  • Rate limiting prevents resource starvation from malicious actors
  • Horizontal scaling strategy with load balancer-ready stateless design

Search Performance at Scale

Challenge: Text search across thousands of posts with multiple filters (location, category, date) must remain fast.

Solution: Implemented compound indexes covering common query patterns and MongoDB aggregation pipeline for complex searches. Future optimization plan includes Elasticsearch integration for full-text search once post volume exceeds 100K records.

Mobile Responsive Design

Mobile HomepageMobile PostMobile Map

Fully responsive design with touch-optimized map controls, collapsible filters, and mobile-first form layouts. CSS Grid and Flexbox ensure consistent layouts across devices. Breakpoints at 768px and 1024px accommodate tablets and desktops.

Installation

Prerequisites

  • Node.js 18+ and npm
  • MongoDB 5.0+
  • Redis 6.0+
  • Cloudinary account (free tier sufficient)

Setup Instructions

# Clone repository
git clone https://github.com/Rotis-Web/lostfound.git
cd lostfound
# Install frontend dependenciescd client
npm install
# Install backend dependenciescd ../server
npm install
# Start MongoDB and Redis (if running locally)# macOS with Homebrew:
brew services start mongodb-community
brew services start redis
# Run development servers
npm run dev:all
# This starts both frontend (port 3000) and backend (port 8000)

Environment Configuration

Frontend Configuration

Create client/.env.local:

NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name

Backend Configuration

Create server/.env:

# Server
PORT=8000
NODE_ENV=development
# Database
MONGO_URI=mongodb://localhost:27017/lostfound
# Production: mongodb+srv://username:password@cluster.mongodb.net/lostfound# Redis
REDIS_URL=redis://localhost:6379
# Production: redis://username:password@host:port# Application URLs
APP_ORIGIN=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# JWT Configuration (generate random 32+ char strings)
JWT_SECRET=your_secure_secret_min_32_chars_use_openssl_rand
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret_different_from_above
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary (sign up at cloudinary.com)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_specific_password

Generating Secure Secrets:

# Use OpenSSL to generate random secrets
openssl rand -base64 32

Design Philosophy

The interface prioritizes clarity and accessibility with a warm color palette that conveys hope and urgency. The design system balances vibrant accent colors with professional neutrals to create an approachable yet trustworthy aesthetic.

Color Palette

ColorHexUsage
Yellow Primary#ffd700CTAs, highlights - conveys energy and optimism
Dark Blue#2c3e60Headers, text - inspires trust and professionalism
Orange Accent#f57a4eImportant buttons, alerts - draws attention
Green Success#51e188Success messages, resolved posts
Red Alert#ff4444Error states, urgent actions
Neutral Gray#9ca3afSecondary text, borders, disabled states

Typography

  • Font Family: Geist Sans - Modern, highly legible sans-serif optimized for UI
  • Heading Scale: 2.5rem / 2rem / 1.5rem / 1.25rem / 1rem
  • Body Text: 1rem (16px) with 1.5 line height for optimal readability
  • Code/Monospace: Geist Mono for technical content

Built to reunite people with what matters most

TypeScriptNext.jsExpressMongoDBRedis

License: MIT | Developer: Alexandru Rotar

About

Lost & Found is a full-stack web application designed to help users recover lost items and pets through intelligent geolocation-based matching

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages