feat: Add POST /api/v1/families endpoint for creating new families
Some checks failed
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled

- Created CreateFamilyDto to validate family creation requests
- Added createFamily() service method with duplicate family check
- Implemented POST /api/v1/families controller endpoint
- Auto-creates family with parent role and full permissions
- Fixes 404 error during onboarding flow

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Andrei
2025-10-09 23:21:06 +00:00
parent fa8db55f93
commit 0d6b901995
3 changed files with 77 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
import { IsString, IsNotEmpty, MaxLength } from 'class-validator';
export class CreateFamilyDto {
@IsString()
@IsNotEmpty()
@MaxLength(100)
name: string;
}

View File

@@ -14,12 +14,33 @@ import {
import { FamiliesService } from './families.service'; import { FamiliesService } from './families.service';
import { InviteFamilyMemberDto } from './dto/invite-family-member.dto'; import { InviteFamilyMemberDto } from './dto/invite-family-member.dto';
import { JoinFamilyDto } from './dto/join-family.dto'; import { JoinFamilyDto } from './dto/join-family.dto';
import { CreateFamilyDto } from './dto/create-family.dto';
import { FamilyRole } from '../../database/entities/family-member.entity'; import { FamilyRole } from '../../database/entities/family-member.entity';
@Controller('api/v1/families') @Controller('api/v1/families')
export class FamiliesController { export class FamiliesController {
constructor(private readonly familiesService: FamiliesService) {} constructor(private readonly familiesService: FamiliesService) {}
/**
* Create a new family
* POST /api/v1/families
* Body: { name: string }
*/
@Post()
async createFamily(@Req() req: any, @Body() createFamilyDto: CreateFamilyDto) {
const family = await this.familiesService.createFamily(
req.user.userId,
createFamilyDto,
);
return {
success: true,
data: {
family,
},
};
}
@Post('invite') @Post('invite')
async inviteMember( async inviteMember(
@Req() req: any, @Req() req: any,

View File

@@ -15,6 +15,7 @@ import {
import { User } from '../../database/entities/user.entity'; import { User } from '../../database/entities/user.entity';
import { InviteFamilyMemberDto } from './dto/invite-family-member.dto'; import { InviteFamilyMemberDto } from './dto/invite-family-member.dto';
import { JoinFamilyDto } from './dto/join-family.dto'; import { JoinFamilyDto } from './dto/join-family.dto';
import { CreateFamilyDto } from './dto/create-family.dto';
import { EmailService } from '../../common/services/email.service'; import { EmailService } from '../../common/services/email.service';
@Injectable() @Injectable()
@@ -29,6 +30,53 @@ export class FamiliesService {
private emailService: EmailService, private emailService: EmailService,
) {} ) {}
/**
* Create a new family
*/
async createFamily(
userId: string,
createFamilyDto: CreateFamilyDto,
): Promise<Family> {
// Check if user already has a family
const existingMembership = await this.familyMemberRepository.findOne({
where: { userId },
});
if (existingMembership) {
throw new ConflictException(
'You are already a member of a family. Leave your current family before creating a new one.',
);
}
// Create the family (ID and shareCode are auto-generated via @BeforeInsert)
const family = this.familyRepository.create({
name: createFamilyDto.name,
shareCodeExpiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
createdBy: userId,
subscriptionTier: 'free', // Default tier
});
const savedFamily = await this.familyRepository.save(family);
// Add creator as a parent (admin) of the family
const creatorMembership = this.familyMemberRepository.create({
userId,
familyId: savedFamily.id,
role: FamilyRole.PARENT,
permissions: {
canAddChildren: true,
canEditChildren: true,
canLogActivities: true,
canViewReports: true,
canInviteMembers: true,
},
});
await this.familyMemberRepository.save(creatorMembership);
return savedFamily;
}
async inviteMember( async inviteMember(
userId: string, userId: string,
familyId: string, familyId: string,