The Journey Behind EventSnap
Inspiration
EventSnap was born from a frustrating personal experience at my sister's wedding. As the unofficial photographer, I spent weeks after the event collecting photos from family members through various channels - WhatsApp, email, Google Drive links, and even physical USB drives. This fragmented process made me realize there had to be a better way to collect and organize event photos.
The vision was clear: create a platform where guests could easily share photos while event owners could effortlessly manage them - all without requiring guests to download apps or create accounts.
Learning Process
Building EventSnap pushed me to deepen my understanding in several key areas:
Multi-tenant Architecture: Implementing a secure system where different user roles (admins, event owners, and anonymous guests) could interact with the same platform while maintaining proper access control.
Row Level Security: Learning how to implement database-level security in Supabase, ensuring that users could only access data they were authorized to see.
Anonymous Authentication Flows: Creating a secure yet frictionless experience for guests to upload photos without registration.
File Management at Scale: Handling potentially large volumes of image uploads, validations, and storage efficiently.
Development Approach
I approached the development with a focus on user experience first:
- Started with user journey mapping for all three user types (admins, event owners, and guests)
- Designed the database schema to support the core functionality while maintaining security
- Built the authentication system with role-based access control
- Implemented the photo upload and management features
- Added QR code generation for easy sharing
- Polished the UI/UX with responsive design
Technical Challenges
The journey wasn't without obstacles:
Infinite Recursion in RLS Policies
One of the most challenging issues was an infinite recursion bug in the Row Level Security policies. When checking if a user was an admin, the policy would query the profiles table, which itself had RLS enabled, creating a circular dependency.
The solution required creating a SECURITY DEFINER function called is_admin() that bypassed RLS when checking admin status:
CREATE OR REPLACE FUNCTION is_admin()
RETURNS boolean
LANGUAGE sql
SECURITY DEFINER
SET search_path = public
AS $$
SELECT EXISTS (
SELECT 1 FROM profiles
WHERE id = auth.uid() AND role = 'admin'
);
$$;
Anonymous Upload Security
Balancing security with ease of use for anonymous uploads was challenging. I needed to ensure guests could upload photos without authentication while preventing abuse.
The solution combined:
- Time-limited access through event deadlines
- File validation on both client and server
- Storage bucket policies with proper constraints
- IP tracking for basic abuse prevention
Efficient Photo Management
As events could potentially accumulate hundreds or thousands of photos, efficient management became crucial. I implemented:
- Lazy loading and pagination for photo galleries
- Client-side image compression before upload
- Thumbnail generation for faster gallery loading
- Bulk download functionality with ZIP compression
Future Directions
This MVP is just the beginning. The architecture was designed with extensibility in mind, allowing for future enhancements like:
- AI-powered duplicate detection
- Facial recognition for photo organization
- Mobile apps for offline uploads
- Social features like commenting and favorites
What started as a solution to a personal problem has evolved into a platform with potential to transform how we collect and share memories from special events.
Built With
- autoprefixer
- date-fns
- eslint
- input-validation
- jszip
- lucide-react
- netlify
- next.js-13.5.1-with-app-router
- postcss
- postgresql-database
- qrcode.react
- radix-ui-components
- react-18.2.0
- react-hook-form-7.53.0-with-zod-validation
- row-level-security-(rls)
- sonner-toast-notifications
- storage)
- supabase-(authentication
- tailwind-css-3.3.3
- typescript-5.2.2
Log in or sign up for Devpost to join the conversation.