diff options
| author | Anjana Vakil <contact@anjana.dev> | 2025-08-26 12:40:16 -0500 |
|---|---|---|
| committer | Anjana Vakil <contact@anjana.dev> | 2025-08-26 12:40:16 -0500 |
| commit | 1dc4f56425209d4ce1d7bb78ec8b5e7b5a755a82 (patch) | |
| tree | 58d06cd695ae17302daff7a87d9096f1d39ea54a /backend/src | |
reset
Diffstat (limited to 'backend/src')
| -rw-r--r-- | backend/src/data/events.json | 162 | ||||
| -rw-r--r-- | backend/src/data/rsvps.json | 452 | ||||
| -rw-r--r-- | backend/src/data/users.json | 152 | ||||
| -rw-r--r-- | backend/src/db.js | 72 | ||||
| -rw-r--r-- | backend/src/routes/api.js | 12 | ||||
| -rw-r--r-- | backend/src/routes/events.js | 97 | ||||
| -rw-r--r-- | backend/src/routes/users.js | 65 | ||||
| -rw-r--r-- | backend/src/server.js | 40 |
8 files changed, 1052 insertions, 0 deletions
diff --git a/backend/src/data/events.json b/backend/src/data/events.json new file mode 100644 index 0000000..da5b0a9 --- /dev/null +++ b/backend/src/data/events.json @@ -0,0 +1,162 @@ +[ + { + "id": "1", + "title": "Community Thanksgiving Potluck", + "date": "2025-11-29", + "image_url": "https://images.unsplash.com/photo-1574672280600-4accfa5b6f98?w=500", + "description": "Join our annual community feast! Bring your favorite dish to share and celebrate gratitude with neighbors. All are welcome to this warm gathering of food and friendship.", + "host_id": "1" + }, + { + "id": "2", + "title": "Solstice Celebration", + "date": "2025-12-21", + "image_url": "https://images.unsplash.com/photo-1482517967863-00e15c9b44be?w=500", + "description": "Welcome the winter solstice with music, meditation, and seasonal ceremonies. Experience the magic of the longest night of the year in community.", + "host_id": "2" + }, + { + "id": "3", + "title": "EventExpo 2026", + "date": "2026-04-01", + "image_url": "https://images.unsplash.com/photo-1511578314322-379afb476865?w=500", + "description": "Discover the future of event planning at EventExpo 2026. Network with industry leaders, explore cutting-edge technologies, and attend inspiring workshops.", + "host_id": "3" + }, + { + "id": "4", + "title": "Critical Mass Bike Ride", + "date": "2025-09-25", + "image_url": "https://images.unsplash.com/photo-1593259525326-ef1511674267?w=500", + "description": "Take back the streets in this monthly community bike ride. Promote cycling awareness and safety while enjoying a scenic tour through the city with fellow cyclists.", + "host_id": "4" + }, + { + "id": "5", + "title": "New Year Celebration", + "date": "2026-01-01", + "image_url": "https://images.unsplash.com/photo-1521478413868-1bbd982fa4a5?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + "description": "Celebrate the start of the new year with fireworks, music, and community festivities.", + "host_id": "5" + }, + { + "id": "6", + "title": "Summer Music Festival", + "date": "2025-07-15", + "image_url": "https://images.unsplash.com/photo-1589553975453-d7e36c7cfbf8?w=500", + "description": "Enjoy a day of live music, food trucks, and fun activities at the annual Summer Music Festival.", + "host_id": "2" + }, + { + "id": "7", + "title": "Tech Startup Meetup", + "date": "2025-09-15", + "image_url": "https://images.unsplash.com/photo-1515187029135-18ee286d815b?w=500", + "description": "Connect with fellow entrepreneurs, developers, and investors. Share ideas, get feedback, and build meaningful relationships in the tech community.", + "host_id": "6" + }, + { + "id": "8", + "title": "Yoga in the Park", + "date": "2025-08-08", + "image_url": "https://images.unsplash.com/photo-1544367567-0f2fcb009e0b?w=500", + "description": "Start your day with a refreshing yoga session in the beautiful city park. All skill levels welcome. Don't forget to bring your mat!", + "host_id": "7" + }, + { + "id": "9", + "title": "Art Gallery Opening", + "date": "2025-10-14", + "image_url": "https://images.unsplash.com/photo-1541961017774-22349e4a1262?w=500", + "description": "Join us for the opening of 'Urban Perspectives' featuring local artists. Enjoy wine, hors d'oeuvres, and engaging conversations about contemporary art.", + "host_id": "8" + }, + { + "id": "10", + "title": "Cooking Class: Mediterranean Cuisine", + "date": "2025-09-28", + "image_url": "https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?w=500", + "description": "Learn to cook authentic Mediterranean dishes from scratch. Includes ingredients, recipes, and a delicious meal to enjoy together.", + "host_id": "9" + }, + { + "id": "11", + "title": "Book Club: Science Fiction", + "date": "2025-08-20", + "image_url": "https://images.unsplash.com/photo-1481627834876-b7833e8f5570?w=500", + "description": "Discuss this month's selection: 'The Three-Body Problem' by Liu Cixin. Coffee and snacks provided. New members always welcome!", + "host_id": "10" + }, + { + "id": "12", + "title": "Photography Workshop", + "date": "2025-11-22", + "image_url": "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?w=500", + "description": "Master the basics of digital photography. Learn composition, lighting, and editing techniques. Bring your camera or smartphone.", + "host_id": "11" + }, + { + "id": "13", + "title": "Language Exchange: Spanish/English", + "date": "2025-09-10", + "image_url": "https://images.unsplash.com/photo-1546410531-bb4caa6b424d?w=500", + "description": "Practice Spanish and English in a friendly, conversational setting. All levels welcome. Coffee and light refreshments included.", + "host_id": "12" + }, + { + "id": "14", + "title": "Board Game Night", + "date": "2025-10-08", + "image_url": "https://images.unsplash.com/photo-1610890716171-6b1bb98ffd09?w=500", + "description": "Join us for an evening of strategy, fun, and friendly competition. We'll have a variety of games for all skill levels.", + "host_id": "13" + }, + { + "id": "15", + "title": "Hiking Adventure", + "date": "2025-07-12", + "image_url": "https://images.unsplash.com/photo-1551632811-561732d1e306?w=500", + "description": "Explore the beautiful mountain trails with our experienced guide. Moderate difficulty, 4-hour hike with stunning views.", + "host_id": "14" + }, + { + "id": "16", + "title": "Jazz Night at the Lounge", + "date": "2025-09-25", + "image_url": "https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?w=500", + "description": "Enjoy live jazz music in an intimate setting. Featuring local musicians and craft cocktails. Reservations recommended.", + "host_id": "15" + }, + { + "id": "17", + "title": "Volunteer Day: Community Garden", + "date": "2025-10-15", + "image_url": "https://images.unsplash.com/photo-1416879595882-3373a0480b5b?w=500", + "description": "Help maintain our community garden. Tasks include planting, weeding, and general maintenance. Tools provided, bring work gloves.", + "host_id": "16" + }, + { + "id": "18", + "title": "Film Screening: Documentary Night", + "date": "2025-08-15", + "image_url": "https://images.unsplash.com/photo-1489599835382-957593cb2371?w=500", + "description": "Watch and discuss thought-provoking documentaries about environmental conservation. Popcorn and refreshments provided.", + "host_id": "17" + }, + { + "id": "19", + "title": "Dance Workshop: Salsa Basics", + "date": "2025-09-18", + "image_url": "https://images.unsplash.com/photo-1504609773096-104ff2c73ba4?w=500", + "description": "Learn the fundamentals of salsa dancing in a fun, beginner-friendly environment. No partner required, comfortable shoes recommended.", + "host_id": "18" + }, + { + "id": "20", + "title": "Craft Fair & Market", + "date": "2025-12-05", + "image_url": "https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=500", + "description": "Support local artisans and discover unique handmade goods. Food trucks, live music, and family-friendly activities throughout the day.", + "host_id": "19" + } +]
\ No newline at end of file diff --git a/backend/src/data/rsvps.json b/backend/src/data/rsvps.json new file mode 100644 index 0000000..eb9c868 --- /dev/null +++ b/backend/src/data/rsvps.json @@ -0,0 +1,452 @@ +[ + { + "event_id": 1, + "name": "Carlos Mendoza", + "email": "carlos.mendoza@example.com" + }, + { + "event_id": 1, + "name": "Yuki Tanaka", + "email": "yuki.tanaka@example.com" + }, + { + "event_id": 1, + "name": "Mohammed Ali", + "email": "mohammed.ali@example.com" + }, + { + "event_id": 1, + "name": "Amina Jafari", + "email": "amina.jafari@example.com" + }, + { + "event_id": 1, + "name": "Ravi Kumar", + "email": "ravi.kumar@example.com" + }, + { + "event_id": 1, + "name": "Sarah Mitchell", + "email": "sarah.mitchell@example.com" + }, + { + "event_id": 1, + "name": "Alex Thompson", + "email": "alex.thompson@example.com" + }, + { + "event_id": 2, + "name": "Aisha Khan", + "email": "aisha.khan@example.com" + }, + { + "event_id": 2, + "name": "Chloe Dubois", + "email": "chloe.dubois@example.com" + }, + { + "event_id": 2, + "name": "Nia Mwangi", + "email": "nia.mwangi@example.com" + }, + { + "event_id": 2, + "name": "Kofi Mensah", + "email": "kofi.mensah@example.com" + }, + { + "event_id": 2, + "name": "Tariq Aziz", + "email": "tariq.aziz@example.com" + }, + { + "event_id": 2, + "name": "Maria Rodriguez", + "email": "maria.rodriguez@example.com" + }, + { + "event_id": 3, + "name": "Liam O'Connor", + "email": "liam.oconnor@example.com" + }, + { + "event_id": 3, + "name": "Santiago Garcia", + "email": "santiago.garcia@example.com" + }, + { + "event_id": 3, + "name": "Priya Singh", + "email": "priya.singh@example.com" + }, + { + "event_id": 3, + "name": "Elena Petrova", + "email": "elena.petrova@example.com" + }, + { + "event_id": 3, + "name": "James Anderson", + "email": "james.anderson@example.com" + }, + { + "event_id": 3, + "name": "Lisa Park", + "email": "lisa.park@example.com" + }, + { + "event_id": 4, + "name": "Fatima Al-Sayed", + "email": "fatima.alsayed@example.com" + }, + { + "event_id": 4, + "name": "Omar Faruk", + "email": "omar.faruk@example.com" + }, + { + "event_id": 4, + "name": "Lina Chen", + "email": "lina.chen@example.com" + }, + { + "event_id": 4, + "name": "Jamal Carter", + "email": "jamal.carter@example.com" + }, + { + "event_id": 4, + "name": "David Johnson", + "email": "david.johnson@example.com" + }, + { + "event_id": 5, + "name": "Raj Patel", + "email": "raj.patel@example.com" + }, + { + "event_id": 5, + "name": "Zara Khan", + "email": "zara.khan@example.com" + }, + { + "event_id": 5, + "name": "Sofia Martinez", + "email": "sofia.martinez@example.com" + }, + { + "event_id": 5, + "name": "Emma Wilson", + "email": "emma.wilson@example.com" + }, + { + "event_id": 6, + "name": "Sofia Rossi", + "email": "sofia.rossi@example.com" + }, + { + "event_id": 6, + "name": "Emma Wilson", + "email": "emma.wilson@example.com" + }, + { + "event_id": 6, + "name": "Liam Smith", + "email": "liam.smith@example.com" + }, + { + "event_id": 6, + "name": "Nina Schmidt", + "email": "nina.schmidt@example.com" + }, + { + "event_id": 6, + "name": "Marcus Johnson", + "email": "marcus.johnson@example.com" + }, + { + "event_id": 7, + "name": "Alex Thompson", + "email": "alex.thompson@example.com" + }, + { + "event_id": 7, + "name": "James Anderson", + "email": "james.anderson@example.com" + }, + { + "event_id": 7, + "name": "Lisa Park", + "email": "lisa.park@example.com" + }, + { + "event_id": 7, + "name": "Elena Petrova", + "email": "elena.petrova@example.com" + }, + { + "event_id": 8, + "name": "Yuki Tanaka", + "email": "yuki.tanaka@example.com" + }, + { + "event_id": 8, + "name": "Chloe Dubois", + "email": "chloe.dubois@example.com" + }, + { + "event_id": 8, + "name": "Sarah Mitchell", + "email": "sarah.mitchell@example.com" + }, + { + "event_id": 8, + "name": "Nina Schmidt", + "email": "nina.schmidt@example.com" + }, + { + "event_id": 9, + "name": "Priya Singh", + "email": "priya.singh@example.com" + }, + { + "event_id": 9, + "name": "Maria Rodriguez", + "email": "maria.rodriguez@example.com" + }, + { + "event_id": 9, + "name": "Lina Chen", + "email": "lina.chen@example.com" + }, + { + "event_id": 9, + "name": "Sofia Garcia", + "email": "sofia.garcia@example.com" + }, + { + "event_id": 10, + "name": "Carlos Mendoza", + "email": "carlos.mendoza@example.com" + }, + { + "event_id": 10, + "name": "Fatima Al-Farsi", + "email": "fatima.alfarsi@example.com" + }, + { + "event_id": 10, + "name": "Omar Faruk", + "email": "omar.faruk@example.com" + }, + { + "event_id": 10, + "name": "Tariq Aziz", + "email": "tariq.aziz@example.com" + }, + { + "event_id": 11, + "name": "Liam O'Connor", + "email": "liam.oconnor@example.com" + }, + { + "event_id": 11, + "name": "Ravi Kumar", + "email": "ravi.kumar@example.com" + }, + { + "event_id": 11, + "name": "Elena Petrova", + "email": "elena.petrova@example.com" + }, + { + "event_id": 11, + "name": "James Anderson", + "email": "james.anderson@example.com" + }, + { + "event_id": 12, + "name": "Yuki Tanaka", + "email": "yuki.tanaka@example.com" + }, + { + "event_id": 12, + "name": "Lina Chen", + "email": "lina.chen@example.com" + }, + { + "event_id": 12, + "name": "Sarah Mitchell", + "email": "sarah.mitchell@example.com" + }, + { + "event_id": 12, + "name": "Alex Thompson", + "email": "alex.thompson@example.com" + }, + { + "event_id": 13, + "name": "Sofia Garcia", + "email": "sofia.garcia@example.com" + }, + { + "event_id": 13, + "name": "Carlos Mendoza", + "email": "carlos.mendoza@example.com" + }, + { + "event_id": 13, + "name": "Maria Rodriguez", + "email": "maria.rodriguez@example.com" + }, + { + "event_id": 13, + "name": "Omar Faruk", + "email": "omar.faruk@example.com" + }, + { + "event_id": 14, + "name": "Jamal Carter", + "email": "jamal.carter@example.com" + }, + { + "event_id": 14, + "name": "David Johnson", + "email": "david.johnson@example.com" + }, + { + "event_id": 14, + "name": "Marcus Johnson", + "email": "marcus.johnson@example.com" + }, + { + "event_id": 14, + "name": "James Anderson", + "email": "james.anderson@example.com" + }, + { + "event_id": 15, + "name": "Kofi Mensah", + "email": "kofi.mensah@example.com" + }, + { + "event_id": 15, + "name": "Nia Mwangi", + "email": "nia.mwangi@example.com" + }, + { + "event_id": 15, + "name": "Sarah Mitchell", + "email": "sarah.mitchell@example.com" + }, + { + "event_id": 15, + "name": "Alex Thompson", + "email": "alex.thompson@example.com" + }, + { + "event_id": 16, + "name": "Nina Schmidt", + "email": "nina.schmidt@example.com" + }, + { + "event_id": 16, + "name": "Chloe Dubois", + "email": "chloe.dubois@example.com" + }, + { + "event_id": 16, + "name": "Emma Wilson", + "email": "emma.wilson@example.com" + }, + { + "event_id": 16, + "name": "Lisa Park", + "email": "lisa.park@example.com" + }, + { + "event_id": 17, + "name": "Fatima Al-Farsi", + "email": "fatima.alfarsi@example.com" + }, + { + "event_id": 17, + "name": "Omar Faruk", + "email": "omar.faruk@example.com" + }, + { + "event_id": 17, + "name": "Tariq Aziz", + "email": "tariq.aziz@example.com" + }, + { + "event_id": 17, + "name": "Carlos Mendoza", + "email": "carlos.mendoza@example.com" + }, + { + "event_id": 18, + "name": "Elena Petrova", + "email": "elena.petrova@example.com" + }, + { + "event_id": 18, + "name": "Ravi Kumar", + "email": "ravi.kumar@example.com" + }, + { + "event_id": 18, + "name": "James Anderson", + "email": "james.anderson@example.com" + }, + { + "event_id": 18, + "name": "Liam O'Connor", + "email": "liam.oconnor@example.com" + }, + { + "event_id": 19, + "name": "Sofia Garcia", + "email": "sofia.garcia@example.com" + }, + { + "event_id": 19, + "name": "Maria Rodriguez", + "email": "maria.rodriguez@example.com" + }, + { + "event_id": 19, + "name": "Chloe Dubois", + "email": "chloe.dubois@example.com" + }, + { + "event_id": 19, + "name": "Nina Schmidt", + "email": "nina.schmidt@example.com" + }, + { + "event_id": 20, + "name": "Emma Wilson", + "email": "emma.wilson@example.com" + }, + { + "event_id": 20, + "name": "Lisa Park", + "email": "lisa.park@example.com" + }, + { + "event_id": 20, + "name": "Sarah Mitchell", + "email": "sarah.mitchell@example.com" + }, + { + "event_id": 20, + "name": "Yuki Tanaka", + "email": "yuki.tanaka@example.com" + }, + { + "event_id": 20, + "name": "Lina Chen", + "email": "lina.chen@example.com" + } +]
\ No newline at end of file diff --git a/backend/src/data/users.json b/backend/src/data/users.json new file mode 100644 index 0000000..0615d81 --- /dev/null +++ b/backend/src/data/users.json @@ -0,0 +1,152 @@ +[ + { + "id": 1, + "username": "amina", + "name": "Amina Khan", + "email": "amina.khan@example.com" + }, + { + "id": 2, + "username": "liam", + "name": "Liam O'Connor", + "email": "liam.oconnor@example.com" + }, + { + "id": 3, + "username": "sofia", + "name": "Sofia Garcia", + "email": "sofia.garcia@example.com" + }, + { + "id": 4, + "username": "raj", + "name": "Raj Patel", + "email": "raj.patel@example.com" + }, + { + "id": 5, + "username": "fatima", + "name": "Fatima Al-Farsi", + "email": "fatima.alfarsi@example.com" + }, + { + "id": 6, + "username": "marcus", + "name": "Marcus Johnson", + "email": "marcus.johnson@example.com" + }, + { + "id": 7, + "username": "yuki", + "name": "Yuki Tanaka", + "email": "yuki.tanaka@example.com" + }, + { + "id": 8, + "username": "priya", + "name": "Priya Singh", + "email": "priya.singh@example.com" + }, + { + "id": 9, + "username": "carlos", + "name": "Carlos Mendoza", + "email": "carlos.mendoza@example.com" + }, + { + "id": 10, + "username": "emma", + "name": "Emma Wilson", + "email": "emma.wilson@example.com" + }, + { + "id": 11, + "username": "omar", + "name": "Omar Faruk", + "email": "omar.faruk@example.com" + }, + { + "id": 12, + "username": "lina", + "name": "Lina Chen", + "email": "lina.chen@example.com" + }, + { + "id": 13, + "username": "david", + "name": "David Johnson", + "email": "david.johnson@example.com" + }, + { + "id": 14, + "username": "chloe", + "name": "Chloe Dubois", + "email": "chloe.dubois@example.com" + }, + { + "id": 15, + "username": "kofi", + "name": "Kofi Mensah", + "email": "kofi.mensah@example.com" + }, + { + "id": 16, + "username": "elena", + "name": "Elena Petrova", + "email": "elena.petrova@example.com" + }, + { + "id": 17, + "username": "jamal", + "name": "Jamal Carter", + "email": "jamal.carter@example.com" + }, + { + "id": 18, + "username": "nina", + "name": "Nina Schmidt", + "email": "nina.schmidt@example.com" + }, + { + "id": 19, + "username": "ravi", + "name": "Ravi Kumar", + "email": "ravi.kumar@example.com" + }, + { + "id": 20, + "username": "tariq", + "name": "Tariq Aziz", + "email": "tariq.aziz@example.com" + }, + { + "id": 21, + "username": "sarah", + "name": "Sarah Mitchell", + "email": "sarah.mitchell@example.com" + }, + { + "id": 22, + "username": "alex", + "name": "Alex Thompson", + "email": "alex.thompson@example.com" + }, + { + "id": 23, + "username": "maria", + "name": "Maria Rodriguez", + "email": "maria.rodriguez@example.com" + }, + { + "id": 24, + "username": "james", + "name": "James Anderson", + "email": "james.anderson@example.com" + }, + { + "id": 25, + "username": "lisa", + "name": "Lisa Park", + "email": "lisa.park@example.com" + } +]
\ No newline at end of file diff --git a/backend/src/db.js b/backend/src/db.js new file mode 100644 index 0000000..f20cac1 --- /dev/null +++ b/backend/src/db.js @@ -0,0 +1,72 @@ +import Database from 'better-sqlite3'; +import EVENTS from './data/events.json' with {type: 'json'}; +import USERS from './data/users.json' with {type: 'json'}; +import RSVPS from './data/rsvps.json' with {type: 'json'}; + + +const db = new Database('src/sqlite.db', { verbose: console.log }); + +console.log(`Initializing database: ${db.name} `); + + +db.pragma('foreign_keys = ON'); + + +db.exec(` + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL UNIQUE, + name TEXT NOT NULL, + email TEXT + ); + `); +db.exec(` + CREATE TABLE IF NOT EXISTS events ( + id INTEGER PRIMARY KEY, + title TEXT NOT NULL, + description TEXT, + image_url TEXT, + date DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + host_id INTEGER REFERENCES users NOT NULL + ); + CREATE INDEX IF NOT EXISTS eventhosts ON events(host_id); + `); + + +const upsertUser = db.prepare(` + INSERT INTO users VALUES (@id, @username, @name, @email) + ON CONFLICT(id) DO NOTHING + `) + +USERS.map((user) => upsertUser.run(user)); + +const upsertEvent = db.prepare(` + INSERT INTO events VALUES (@id, @title, @description, @image_url, @date, @host_id) + ON CONFLICT(id) DO NOTHING + `) + +EVENTS.map((event) => { + upsertEvent.run(event); +}); + + +db.exec(` + CREATE TABLE IF NOT EXISTS rsvps ( + event_id INTEGER REFERENCES events NOT NULL, + name TEXT NOT NULL, + email TEXT NOT NULL, + UNIQUE(event_id, email) ON CONFLICT REPLACE + ); + CREATE INDEX IF NOT EXISTS rsvpevents ON rsvps(event_id); +`); + +const upsertRSVP = db.prepare(` + INSERT INTO rsvps VALUES (@event_id, @name, @email) +`); +RSVPS.map((rsvp) => { + upsertRSVP.run(rsvp); +}); + + + +export default db;
\ No newline at end of file diff --git a/backend/src/routes/api.js b/backend/src/routes/api.js new file mode 100644 index 0000000..2eb07d1 --- /dev/null +++ b/backend/src/routes/api.js @@ -0,0 +1,12 @@ +import { Router } from 'express'; +import usersRouter from './users.js'; +import eventsRouter from './events.js'; + + +const router = Router(); + +router.use('/users', usersRouter); +router.use('/events', eventsRouter); + + +export default router; diff --git a/backend/src/routes/events.js b/backend/src/routes/events.js new file mode 100644 index 0000000..b95c747 --- /dev/null +++ b/backend/src/routes/events.js @@ -0,0 +1,97 @@ +import db from '../db.js'; +import { Router } from 'express'; +import { getUser } from './users.js'; + +const router = Router(); + +const joinHost = (event) => { + const host = getUser(event.host_id); + return { ...event, host }; +} + +const joinRSVPs = (event) => { + const { id } = event; + const getRSVPs = db.prepare('SELECT * FROM rsvps WHERE event_id = @id'); + const rsvps = getRSVPs.all({ id }); + return { ...event, rsvps }; +} + +const getEvent = (eventId) => { + const byId = db.prepare('SELECT * FROM events WHERE id = @eventId'); + const event = byId.get({ eventId }); + return joinHost(event); +} + +router.get('/', (_req, res) => { + const listEvents = db.prepare(`SELECT * FROM events`); + const events = listEvents.all(); + res.json(events.map(joinHost).map(joinRSVPs)); +}); + +const insertEvent = db.prepare(`INSERT INTO events VALUES (@id, @title, @description, @image_url, @date, @host_id)`); + +router.post('/new', (req, res) => { + const data = req.body; + const { lastInsertRowid: id } = insertEvent.run(data); + const event = getEvent(id); + res.status(201).json(event); +}); + +router.get('/:id', (req, res) => { + const id = parseInt(req.params.id); + const event = getEvent(id); + if (!event) { + return res.status(404).json({ error: 'Event not found' }); + } + res.json(event); +}); + +router.patch('/:id', (req, res) => { + const eventId = parseInt(req.params.id); + const patch = req.body; + + const updateCol = db.prepare(` + UPDATE events SET @col = @val WHERE id = @eventId + `); + const updateEvent = db.transaction((patch) => { + for (const [col, val] of Object.entries(patch)) { + updateCol.run({ col, val, eventId }); + } + }); + + updateEvent(Object.entries(patch)); + const updated = getEvent(eventId); + res.json(updated); +}); + +router.delete('/:id', (req, res) => { + const deleteEvent = db.prepare(`DELETE FROM events WHERE id = @eventId`); + const eventId = parseInt(req.params.id); + const event = getEvent(eventId); + if (!event) { + return res.status(404).json({ error: 'Event not found' }); + } + deleteEvent.run({ eventId }); + res.json(event); +}); + +router.post('/:id/rsvp', (req, res) => { + const eventId = parseInt(req.params.id); + const { name, email } = req.body; + + const getRSVP = db.prepare(`SELECT * FROM rsvps WHERE (event_id = ${eventId} AND email = '${email}')`); + const insertRSVP = db.prepare(`INSERT INTO rsvps VALUES (@eventId, @name, @email)`); + + let [rsvp] = getRSVP.all({ eventId, email }); + if (rsvp) { + // This email has already RSVPed + res.status(200).json({ rsvp }); + } else { + // New RSVP + insertRSVP.run({ name, email, eventId }); + rsvp = getRSVP.run({ eventId, email }); + res.status(201).json({ rsvp }); + } +}); + +export default router; diff --git a/backend/src/routes/users.js b/backend/src/routes/users.js new file mode 100644 index 0000000..6d874e9 --- /dev/null +++ b/backend/src/routes/users.js @@ -0,0 +1,65 @@ +import { Router } from 'express'; +import db from '../db.js'; + +const router = Router(); + +export const getUser = (userId) => { + const byId = db.prepare('SELECT * FROM users WHERE id = @userId'); + return byId.get({ userId }); +} + +router.get('/', (_req, res) => { + const listUsers = db.prepare(`SELECT * FROM users`) + const users = listUsers.all(); + res.json(users); +}); + +router.post('/new', (req, res) => { + const data = req.body; + const cols = Object.keys(data).join(' , '); + const vals = Object.values(data).join(' , '); + const insertUser = db.prepare(`INSERT INTO users(@cols) VALUES (@vals)`); + const { lastInsertRowid: id } = insertUser.run({ cols, vals }); + const user = getUser(id); + res.json(user); +}); + +router.get('/:id', (req, res) => { + const id = req.params.id; + const user = getUser(id); + if (!user) { + res.status(404).json({ error: 'User not found' }); + } + res.json(user); +}); + +router.patch('/:id', (req, res) => { + const userId = req.params.id; + const patch = req.body; + + const updateCol = db.prepare(` + UPDATE users SET @col = @val WHERE id = @userId + `); + const updateUser = db.transaction((patch) => { + for (const [col, val] of Object.entries(patch)) { + updateCol.run(col, val, userId); + }; + }); + + updateUser(Object.entries(patch)); + const updated = getUser(userId); + res.json(updated); +}); + +router.delete('/:id', (req, res) => { + const deleteUser = db.prepare(`DELETE FROM users WHERE id = @userId`) + const userId = parseInt(req.params.id); + const user = getUser(userId); + if (!user) { + res.status(404).json({ error: 'User not found' }); + } + deleteUser.run({ userId }); + res.json(user); +}); + +export default router; diff --git a/backend/src/server.js b/backend/src/server.js new file mode 100644 index 0000000..1861150 --- /dev/null +++ b/backend/src/server.js @@ -0,0 +1,40 @@ +import express from 'express'; +import cookieParser from 'cookie-parser'; +import logger from 'morgan'; +import cors from 'cors'; +import helmet from 'helmet'; + +import apiRouter from './routes/api.js'; + + +const PORT = process.env.PORT || 3000; +const NODE_ENV = process.env.NODE_ENV || 'development'; + +const app = express(); + + +app.use(logger('dev')); +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(helmet()); +app.use(cors({ + origin: 'http://localhost:5173' +})); + +app.use('/api', apiRouter); + +app.use((_req, res) => { + res.status(404).json({ + error: 'Not Found', + endpoints: [ + '/api/events', + '/api/users' + ] + }); +}); + +app.listen(PORT, () => { + console.log(`\nServer listening at http://localhost:${PORT}`); + console.log(`Server running in ${NODE_ENV} mode\n`); +})
\ No newline at end of file |
