updates to multi-family support

This commit is contained in:
John Overton
2025-05-16 09:35:51 -05:00
parent f10bd9dcfb
commit d027efbffe
5 changed files with 2464 additions and 21 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,122 @@
# Multi-Family Support Implementation Summary
This document provides a high-level summary of the changes required to implement multi-family support in the baby tracker application. It brings together the key points from the detailed documentation files.
## Overview
The goal is to transform the application from a single-family system to a multi-family platform where:
1. Each family has its own isolated data
2. Users can belong to multiple families
3. Family data is accessed through family-specific URLs
4. The application maintains proper data isolation and security
## Key Components
### 1. Database Changes ([schema.prisma](../prisma/schema.prisma))
- Added a new `Family` model to represent family entities
- Added `familyId` to all relevant models to establish relationships
- Created a `FamilyMember` model to manage family membership
- Added appropriate indexes for performance optimization
- Made `familyId` nullable initially for migration purposes
### 2. Migration Strategy ([migration-script.md](./migration-script.md))
- Created a migration script to add the Family model and familyId fields
- Implemented a process to create a default family for existing data
- Developed a strategy to assign all existing data to the default family
- Provided verification steps to ensure data integrity
### 3. API Changes ([api-changes.md](./api-changes.md))
- Updated authentication to include family context
- Added middleware for family-based access control
- Modified all API endpoints to include family filtering
- Created new endpoints for family management
- Implemented utility functions for family context
### 4. Frontend Changes ([frontend-changes.md](./frontend-changes.md))
- Created a Family context provider for state management
- Developed a family selector component
- Updated the app layout to include family selection
- Modified all data fetching to include family context
- Created interfaces for family management
- Updated routing to include family slugs
## Implementation Plan ([plan.md](./plan.md))
The implementation is divided into five phases:
1. **Database Schema Updates**
- Add Family model
- Add familyId to existing models
- Create migration scripts
2. **Authentication & Authorization**
- Update auth flow
- Implement family membership
- Add family-based access control
3. **API Routes**
- Update existing endpoints
- Create new family management endpoints
- Implement family-based filtering
4. **Frontend Changes**
- Update app layout
- Modify routing
- Create family management UI
5. **Data Migration**
- Create default family
- Assign existing data
- Verify data integrity
## Technical Approach
### Data Isolation
- Every database query includes familyId filtering
- API middleware verifies family access permissions
- Frontend components only display family-specific data
### User Experience
- Users can switch between families using a dropdown selector
- Family management interface allows creating, editing, and deleting families
- Family-specific URLs provide direct access to family data
### Security
- JWT tokens include familyId for authentication
- Middleware verifies user has access to requested family
- Database queries enforce family-based filtering
## Migration Considerations
- The initial migration makes familyId nullable to avoid breaking existing data
- A migration script creates a default family and assigns all existing data
- After migration, familyId can be made required for data integrity
- Thorough testing is required to ensure no data is lost or corrupted
## Mobile Considerations
When adapting these changes for React Native:
- Replace URL-based navigation with React Navigation
- Use AsyncStorage instead of localStorage
- Adapt UI components for touch interactions
- Maintain the same data isolation principles
## Next Steps
1. Implement the database changes and run initial migration
2. Update the authentication system to include family context
3. Modify API endpoints to enforce family-based filtering
4. Develop the frontend components for family selection and management
5. Test thoroughly with multiple families and users
6. Deploy with feature flags to enable gradual rollout
By following this implementation plan, the application will be transformed into a multi-family platform that maintains proper data isolation while providing a seamless user experience.

View File

@@ -0,0 +1,173 @@
# Multi-Family Migration Script
This document provides a migration script and instructions for transitioning the existing database to support multiple families. The migration process is designed to be minimally disruptive, allowing for a smooth transition from a single-family to a multi-family model.
## Migration Process Overview
1. Create a new Prisma migration that adds the Family model and familyId fields to all relevant models
2. Run the migration to update the database schema
3. Execute a script to create a default family and assign all existing data to it
4. Optionally, make the familyId fields required after the migration is complete
## Step 1: Create Prisma Migration
Run the following command to create a new migration:
```bash
npx prisma migrate dev --name add-multi-family-support
```
This will generate a new migration file based on the changes made to the schema.prisma file.
## Step 2: Migration Script
Create a file named `migrate-to-multi-family.ts` in the `prisma` directory with the following content:
```typescript
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('Starting multi-family migration...');
// Create default family
const defaultFamily = await prisma.family.create({
data: {
name: 'Default Family',
slug: 'default-family',
isActive: true,
},
});
console.log(`Created default family with ID: ${defaultFamily.id}`);
// Get existing settings to use the familyName
const existingSettings = await prisma.settings.findFirst();
if (existingSettings) {
// Update the default family name if settings exist
await prisma.family.update({
where: { id: defaultFamily.id },
data: { name: existingSettings.familyName },
});
console.log(`Updated default family name to: ${existingSettings.familyName}`);
}
// Update all existing records to reference the default family
const models = [
'baby',
'caretaker',
'sleepLog',
'unit',
'feedLog',
'diaperLog',
'moodLog',
'note',
'settings',
'milestone',
'pumpLog',
'playLog',
'bathLog',
'measurement',
'contact',
'calendarEvent',
'medicine',
'medicineLog',
];
for (const model of models) {
const count = await updateModelFamilyId(model, defaultFamily.id);
console.log(`Updated ${count} records in ${model} model`);
}
// Create FamilyMember records for all caretakers
const caretakers = await prisma.caretaker.findMany();
for (const caretaker of caretakers) {
await prisma.familyMember.create({
data: {
familyId: defaultFamily.id,
caretakerId: caretaker.id,
role: caretaker.role === 'ADMIN' ? 'admin' : 'member',
},
});
}
console.log(`Created ${caretakers.length} family member records`);
console.log('Migration completed successfully!');
}
async function updateModelFamilyId(model: string, familyId: string): Promise<number> {
// Use dynamic property access to update each model
const updateResult = await (prisma as any)[model].updateMany({
where: {
familyId: null,
},
data: {
familyId,
},
});
return updateResult.count;
}
main()
.catch((e) => {
console.error('Migration failed:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
```
## Step 3: Run the Migration Script
Execute the migration script with the following command:
```bash
npx ts-node prisma/migrate-to-multi-family.ts
```
This script will:
1. Create a default family
2. Update the family name based on existing settings
3. Update all existing records to reference the default family
4. Create FamilyMember records for all caretakers
## Step 4: Make familyId Required (Optional)
After confirming that the migration was successful and all data is properly associated with the default family, you can make the familyId fields required. This step is optional but recommended for data integrity.
1. Update the schema.prisma file to make familyId required in all models by changing `String?` to `String`
2. Create a new migration:
```bash
npx prisma migrate dev --name make-family-id-required
```
## Verification Steps
After completing the migration, verify that:
1. All records have been properly associated with the default family
2. The application functions correctly with the new multi-family schema
3. New records are properly associated with families
4. Family-based filtering works as expected
## Rollback Plan
If issues are encountered during the migration:
1. Restore the database from backup
2. Revert the schema changes
3. Run `npx prisma migrate resolve --rolled-back add-multi-family-support`
## Additional Considerations
- **API Updates**: Ensure all API endpoints include family-based filtering
- **Authentication**: Update authentication to include family context
- **Frontend**: Update UI to display and manage family information
- **Testing**: Thoroughly test all functionality with multiple families

View File

@@ -13,28 +13,73 @@ model Family {
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
isActive Boolean @default(true)
// Relations
babies Baby[]
caretakers Caretaker[]
settings Settings[]
// Add other relations as needed
babies Baby[]
caretakers Caretaker[]
settings Settings[]
sleepLogs SleepLog[]
feedLogs FeedLog[]
diaperLogs DiaperLog[]
moodLogs MoodLog[]
notes Note[]
milestones Milestone[]
pumpLogs PumpLog[]
playLogs PlayLog[]
bathLogs BathLog[]
measurements Measurement[]
medicines Medicine[]
medicineLogs MedicineLog[]
contacts Contact[]
calendarEvents CalendarEvent[]
units Unit[]
// All other relations should be included here
}
```
### 1.2 Update Existing Models
- Add `familyId` field to all relevant models
- Make `familyId` required for most models
- Set up proper relations
- Add indexes for better query performance
- Add `familyId` field to all models that store family-specific data:
- **Entity models**: Baby, Caretaker, Medicine, Contact
- **Activity logs**: SleepLog, FeedLog, DiaperLog, MoodLog, Note, Milestone, PumpLog, PlayLog, BathLog, Measurement, MedicineLog
- **Configuration**: Settings, Unit (unless units should be shared across families)
- **Events**: CalendarEvent
- Make `familyId` required for all these models
- Set up proper relations with the Family model
- Add indexes for better query performance on familyId fields
- Consider how junction tables (BabyEvent, CaretakerEvent, etc.) will handle family relationships
### 1.3 Migration Strategy
### 1.3 Example Model Update
```prisma
model Baby {
id String @id @default(uuid())
firstName String
lastName String
birthDate DateTime
gender Gender?
inactive Boolean @default(false)
// Other fields...
// Add family relation
family Family @relation(fields: [familyId], references: [id])
familyId String
// Existing relations...
@@index([familyId])
// Other indexes...
}
```
### 1.4 Migration Strategy
- Create migration script to:
- Add Family model
- Add familyId to all models
- Create default family for existing users
- Generate slugs for all families
- Generate slugs for all families (based on family name)
- Assign all existing data to default family
- Ensure referential integrity across all tables
- Handle edge cases where data might need to be split into multiple families
## Phase 2: Authentication & Authorization
@@ -42,19 +87,44 @@ model Family {
- Modify login to handle family selection
- Store selected familyId in session/token
- Update auth context to include family information
- Consider how users will be associated with multiple families (if applicable)
### 2.2 Update Middleware
### 2.2 Family Membership
- Create a model for tracking family membership:
```prisma
model FamilyMember {
family Family @relation(fields: [familyId], references: [id])
familyId String
caretaker Caretaker @relation(fields: [caretakerId], references: [id])
caretakerId String
role String // e.g., "admin", "member"
joinedAt DateTime @default(now())
@@id([familyId, caretakerId])
@@index([familyId])
@@index([caretakerId])
}
```
- Define roles and permissions for family members
- Implement invitation and acceptance flow
### 2.3 Update Middleware
- Add family-based access control
- Verify user has access to requested family
- Automatically add familyId to all database queries
- Create utility functions that enforce family-based filtering
- Handle unauthorized access gracefully
- Consider implementing row-level security if your database supports it
## Phase 3: API Routes
### 3.1 Update Existing Endpoints
- Add family-based filtering to all GET endpoints
- Create middleware that automatically adds familyId to all queries
- Update input validation to include familyId
- Modify responses to be family-scoped
- Add proper error handling
- Add proper error handling for family-related errors
- Ensure all database operations respect family boundaries
### 3.2 New API Endpoints
- `GET /api/families` - List all families for current user
@@ -149,23 +219,35 @@ model Family {
## Future Enhancements
1. Family member roles (admin, member, etc.)
1. Advanced family member roles and permissions
2. Family activity feed
3. Family usage analytics
4. Family-specific settings
5. Family data export
4. Additional family-specific settings
5. Family data export and import
6. Cross-family data sharing (for specific use cases)
7. Family templates for quick setup
## Risks & Mitigations
1. **Performance Impact**
- Add proper indexing
- Monitor query performance
- Add proper indexing on all familyId fields
- Monitor query performance with family filtering
- Consider database sharding for very large deployments
- Optimize queries that join across multiple tables
2. **Data Migration**
- Backup before migration
- Test migration thoroughly
- Test migration thoroughly on a copy of production data
- Create a rollback plan
- Consider a phased migration approach for large datasets
3. **User Experience**
- Provide clear feedback
- Add loading states
- Handle errors gracefully
- Provide clear feedback during family switching
- Add loading states for all family-related operations
- Handle errors gracefully with user-friendly messages
- Ensure UI clearly indicates which family is currently active
4. **Data Isolation**
- Regularly audit database queries to ensure proper family filtering
- Implement automated tests that verify data isolation
- Create monitoring for potential cross-family data leaks