Create server/src/middleware/logger.js:
// ====================================
// Request Logger Middleware
// ====================================
const logger = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
const log = {
method: req.method,
url: req.originalUrl,
status: res.statusCode,
duration: `${duration}ms`,
ip: req.ip,
timestamp: new Date().toISOString()
};
if (res.statusCode >= 400) {
console.error('❌ Request Error:', JSON.stringify(log));
} else if (duration > 2000) {
console.warn('⚠️ Slow Request:', JSON.stringify(log));
}
});
next();
};
module.exports = logger;Add to server.js:
const logger = require('./src/middleware/logger');
app.use(logger);Create server/src/config/seed.js for demo/presentation data:
// ====================================
// Seed Script — Demo Data for Presentation
// Run: npm run seed
// ====================================
require('dotenv').config();
const { createClient } = require('@supabase/supabase-js');
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY
);
async function seed() {
console.log('🌱 Seeding CivicStreak database...\n');
// 1. Verify wards exist
const { data: wards } = await supabase.from('wards').select('id, ward_name');
console.log(`✅ ${wards.length} wards found`);
// 2. Verify tasks exist
const { data: tasks } = await supabase.from('micro_tasks').select('id, title');
console.log(`✅ ${tasks.length} micro-tasks found`);
// 3. Verify achievements exist
const { data: achievements } = await supabase.from('achievements').select('id, name');
console.log(`✅ ${achievements.length} achievements found`);
// 4. Create demo civic issues
if (wards.length > 0) {
const demoIssues = [
{
title: 'Broken foot
{
title: 'Broken footpath on SV Road near Azad Nagar',
description: 'Large section of footpath broken for 3 months. Pedestrians forced to walk on road. Dangerous during monsoon.',
category: 'Infrastructure',
address: 'SV Road, near Azad Nagar, Andheri West',
ward_id: wards[0].id,
status: 'in_progress',
upvotes: 23,
latitude: 19.1364,
longitude: 72.8296,
photo_urls: []
},
{
title: 'Non-functional streetlights on Link Road',
description: '7 out of 10 streetlights not working between Infinity Mall and DN Nagar metro station. Safety hazard at night.',
category: 'Electricity',
address: 'Link Road, Andheri West',
ward_id: wards[0].id,
status: 'resolved',
upvotes: 45,
resolution_date: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
latitude: 19.1340,
longitude: 72.8280,
photo_urls: []
},
{
title: 'Garbage dump near residential area, Lane 3',
description: 'Unauthorized garbage dump has formed near Lane 3. Foul smell and mosquito breeding ground. BMC collection irregular.',
category: 'Sanitation',
address: 'Lane 3, Off SV Road, Andheri West',
ward_id: wards[0].id,
status: 'reported',
upvotes: 18,
latitude: 19.1370,
longitude: 72.8300,
photo_urls: []
},
{
title: 'Tree cutting without permission on Hill Road',
description: 'Several trees being cut near Hill Road junction. No BMC permission board visible. Need immediate investigation.',
category: 'Environment',
address: 'Hill Road, Bandra West',
ward_id: wards.length > 2 ? wards[2].id : wards[0].id,
status: 'reported',
upvotes: 31,
latitude: 19.0596,
longitude: 72.8295,
photo_urls: []
},
{
title: 'Water pipeline leak wasting thousands of liters',
description: 'Major water pipeline leak at junction of Station Road. Water flowing onto road for 2 weeks. Multiple complaints to BMC ignored.',
category: 'Water',
address: 'Station Road, Andheri East',
ward_id: wards.length > 1 ? wards[1].id : wards[0].id,
status: 'acknowledged',
upvotes: 37,
latitude: 19.1197,
longitude: 72.8464,
photo_urls: []
},
{
title: 'Public toilet in non-functional state for months',
description: 'Public toilet near market area completely non-functional. No water supply, broken doors, unhygienic conditions.',
category: 'Sanitation',
address: 'Market Area, Goregaon West',
ward_id: wards.length > 3 ? wards[3].id : wards[0].id,
status: 'stale',
upvotes: 12,
latitude: 19.1663,
longitude: 72.8526,
photo_urls: []
}
];
// Insert demo issues (need a dummy reported_by user)
// We'll create issues without reported_by for seeding
// In production, issues always have a reporter
console.log('\n📢 Inserting demo civic issues...');
for (const issue of demoIssues) {
const { error } = await supabase
.from('civic_issues')
.insert({
...issue,
reported_by: null // Will need a real user ID in production
});
if (error) {
// If reported_by is required, skip seeding issues
console.log(`⚠️ Skipping issue "${issue.title}" (needs reported_by user)`);
} else {
console.log(` ✅ Issue: "${issue.title}"`);
}
}
}
// 5. Insert additional micro-tasks for variety
console.log('\n📋 Adding more micro-tasks...');
const additionalTasks = [
{
title: '📸 Document your nearest public garden',
description: 'Visit the nearest municipal garden in your ward. Photograph its condition — cleanliness, facilities, greenery.',
category: 'DOCUMENT',
difficulty: 'Beginner',
estimated_minutes: 15,
xp_reward: 25,
required_proof: 'photo',
interest_tags: ['Environment'],
instructions: '1. Visit the nearest garden/park.\n2. Take 3-4 photos showing overall condition.\n3. Rate cleanliness from 1-5 in your notes.\n4. Note if water, seating, and playground are available.'
},
{
title: '🗣️ Interview a street vendor about civic issues',
description: 'Talk to a local street vendor and ask about the civic challenges they face daily. Summarize in 3-5 lines.',
category: 'CONNECT',
difficulty: 'Intermediate',
estimated_minutes: 15,
xp_reward: 35,
required_proof: 'text',
interest_tags: ['Community', 'Governance'],
instructions: '1. Find a street vendor in your area.\n2. Ask: What are the biggest civic problems you face?\n3. Ask: Have you tried complaining to BMC?\n4. Write a 3-5 line summary of their response.'
},
{
title: '📊 Check if your ward office has a citizen charter',
description: 'Visit your ward office and check if a Citizen Charter is displayed. Photograph it or note its absence.',
category: 'DOCUMENT',
difficulty: 'Intermediate',
estimated_minutes: 20,
xp_reward: 30,
required_proof: 'photo',
interest_tags: ['Governance'],
instructions: '1. Visit your ward office during working hours.\n2. Look for a "Citizen Charter" board near the entrance.\n3. If found, photograph it clearly.\n4. If not found, note this in your submission.'
},
{
title: '🌊 Rate your area\'s monsoon readiness',
description: 'Walk through your neighborhood and rate monsoon preparedness — check drains, waterlogging spots, tree stability.',
category: 'TRACK',
difficulty: 'Beginner',
estimated_minutes: 15,
xp_reward: 25,
required_proof: 'text',
interest_tags: ['Infrastructure', 'Environment', 'Safety'],
instructions: '1. Walk around your block/area.\n2. Check: Are drains clear or clogged?\n3. Identify spots that usually waterlog.\n4. Note any dangerously leaning trees.\n5. Rate overall monsoon readiness 1-10.'
},
{
title: '📜 Read and summarize one ward committee resolution',
description: 'Find the latest ward committee meeting minutes (online or at ward office) and summarize one resolution.',
category: 'LEARN',
difficulty: 'Advanced',
estimated_minutes: 20,
xp_reward: 45,
required_proof: 'text',
interest_tags: ['Governance', 'Education'],
instructions: '1. Search for your ward committee minutes online (BMC website).\n2. If not available online, visit the ward office.\n3. Read one resolution passed in the latest meeting.\n4. Summarize it in 3-4 lines in simple language.'
},
{
title: '🤝 Share CivicStreak with a friend',
description: 'Introduce one friend to CivicStreak. Help them register and complete their first task.',
category: 'MENTOR',
difficulty: 'Beginner',
estimated_minutes: 10,
xp_reward: 50,
required_proof: 'text',
interest_tags: ['Community', 'Leadership'],
instructions: '1. Share CivicStreak link with a friend.\n2. Help them register and pick their ward.\n3. Guide them through their first Civic Bite.\n4. Share both your names as proof of completion.'
},
{
title: '📝 Write a 50-word letter to your ward councilor',
description: 'Draft a short, polite letter to your ward councilor about the most urgent issue in your area.',
category: 'VOICE',
difficulty: 'Intermediate',
estimated_minutes: 10,
xp_reward: 35,
required_proof: 'text',
interest_tags: ['Governance', 'Community'],
instructions: '1. Think of the most urgent civic issue in your area.\n2. Write a brief, respectful letter (50-100 words).\n3. Address it to "Respected Ward Councilor".\n4. Include the issue, location, and your request.\n5. Submit the letter text here.'
},
{
title: '🔍 Find and list 3 emergency numbers for your ward',
description: 'Research and document 3 important emergency/helpline numbers specific to your ward area.',
category: 'LEARN',
difficulty: 'Beginner',
estimated_minutes: 10,
xp_reward: 20,
required_proof: 'text',
interest_tags: ['Safety', 'Governance'],
instructions: '1. Find: Ward office phone number.\n2. Find: Nearest fire station number.\n3. Find: Local police station number.\n4. Bonus: BMC helpline, water complaint number.\n5. List all numbers with labels.'
}
];
for (const task of additionalTasks) {
const { error } = await supabase
.from('micro_tasks')
.insert({
...task,
is_active: true,
is_recurring: false,
times_completed: Math.floor(Math.random() * 20)
});
if (error) {
console.log(`⚠️ Failed to insert task: ${task.title} — ${error.message}`);
} else {
console.log(` ✅ Task: "${task.title}"`);
}
}
// 6. Update ward responsiveness scores (mock data)
console.log('\n📊 Updating ward responsiveness scores...');
const wardScores = [6.2, 5.8, 7.1, 4.5, 5.0, 6.8, 7.5, 3.9];
for (let i = 0; i < Math.min(wards.length, wardScores.length); i++) {
await supabase
.from('wards')
.update({ responsiveness_score: wardScores[i] })
.eq('id', wards[i].id);
console.log(` ✅ ${wards[i].ward_name}: ${wardScores[i]}/10`);
}
console.log('\n🎉 Seeding complete!\n');
console.log('Summary:');
console.log(` 🏘️ Wards: ${wards.length}`);
console.log(` 📋 Tasks: ${tasks.length + additionalTasks.length}`);
console.log(` 🏅 Achievements: ${achievements.length}`);
console.log(` 📢 Demo Issues: ${6}`);
console.log('\n✨ CivicStreak is ready to go!');
process.exit(0);
}
seed().catch(error => {
console.error('❌ Seeding failed:', error);
process.exit(1);
});Run the seed script:
cd server
npm run seed