feat(db): implement complete database schema and models

- Add Prisma schema with PostgreSQL 15 support
- Create Users, Batches, Images, Payments, ApiKeys tables
- Implement proper foreign key relationships and indexes
- Add enum types for status fields (Plan, BatchStatus, ImageStatus, PaymentStatus)
- Support for JSON fields (vision_tags, metadata)
- UUID primary keys for security
- Created/updated timestamps with proper defaults

Database Layer Components:
- Prisma service with connection management and health checks
- Repository pattern for all entities with comprehensive CRUD operations
- TypeScript DTOs with class-validator decorations
- Swagger API documentation annotations
- Helper functions for business logic (quota management, pricing, etc.)

Development Support:
- Environment variables template
- Database seed script with realistic test data
- TypeScript configuration optimized for Nest.js
- Package.json with all required dependencies

Resolves database requirements from issues §78-81 establishing
the complete data layer foundation for the AI Bulk Image Renamer SaaS.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DustyWalker 2025-08-05 17:02:03 +02:00
parent 90016254a9
commit e7e09d5e2c
15 changed files with 3606 additions and 0 deletions

View file

@ -0,0 +1,203 @@
import {
IsEmail,
IsString,
IsEnum,
IsInt,
IsBoolean,
IsOptional,
IsUUID,
Min,
IsDate
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Plan } from '@prisma/client';
export class CreateUserDto {
@ApiPropertyOptional({
description: 'Google OAuth UID for OAuth integration',
example: 'google_123456789'
})
@IsOptional()
@IsString()
googleUid?: string;
@ApiProperty({
description: 'User email address',
example: 'user@example.com'
})
@IsEmail()
email: string;
@ApiProperty({
description: 'Hashed version of email for privacy',
example: 'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
})
@IsString()
emailHash: string;
@ApiPropertyOptional({
description: 'User subscription plan',
enum: Plan,
default: Plan.BASIC
})
@IsOptional()
@IsEnum(Plan)
plan?: Plan;
@ApiPropertyOptional({
description: 'Remaining quota for current period',
example: 50,
minimum: 0
})
@IsOptional()
@IsInt()
@Min(0)
quotaRemaining?: number;
}
export class UpdateUserDto {
@ApiPropertyOptional({
description: 'User subscription plan',
enum: Plan
})
@IsOptional()
@IsEnum(Plan)
plan?: Plan;
@ApiPropertyOptional({
description: 'Remaining quota for current period',
minimum: 0
})
@IsOptional()
@IsInt()
@Min(0)
quotaRemaining?: number;
@ApiPropertyOptional({
description: 'Whether the user account is active'
})
@IsOptional()
@IsBoolean()
isActive?: boolean;
}
export class UserResponseDto {
@ApiProperty({
description: 'Unique user identifier',
example: '550e8400-e29b-41d4-a716-446655440000'
})
@IsUUID()
id: string;
@ApiPropertyOptional({
description: 'Google OAuth UID',
example: 'google_123456789'
})
@IsOptional()
@IsString()
googleUid?: string;
@ApiProperty({
description: 'User email address',
example: 'user@example.com'
})
@IsEmail()
email: string;
@ApiProperty({
description: 'User subscription plan',
enum: Plan
})
@IsEnum(Plan)
plan: Plan;
@ApiProperty({
description: 'Remaining quota for current period',
example: 50
})
@IsInt()
@Min(0)
quotaRemaining: number;
@ApiProperty({
description: 'Date when quota resets'
})
@IsDate()
quotaResetDate: Date;
@ApiProperty({
description: 'Whether the user account is active'
})
@IsBoolean()
isActive: boolean;
@ApiProperty({
description: 'User creation timestamp'
})
@IsDate()
createdAt: Date;
@ApiProperty({
description: 'User last update timestamp'
})
@IsDate()
updatedAt: Date;
}
export class UserStatsDto {
@ApiProperty({
description: 'Total number of batches processed'
})
@IsInt()
@Min(0)
totalBatches: number;
@ApiProperty({
description: 'Total number of images processed'
})
@IsInt()
@Min(0)
totalImages: number;
@ApiProperty({
description: 'Current quota usage this period'
})
@IsInt()
@Min(0)
quotaUsed: number;
@ApiProperty({
description: 'Total quota for current plan'
})
@IsInt()
@Min(0)
totalQuota: number;
@ApiProperty({
description: 'Percentage of quota used'
})
@IsInt()
@Min(0)
quotaUsagePercentage: number;
}
// Helper function to get quota limits by plan
export function getQuotaLimitForPlan(plan: Plan): number {
switch (plan) {
case Plan.BASIC:
return 50;
case Plan.PRO:
return 500;
case Plan.MAX:
return 1000;
default:
return 50;
}
}
// Helper function to calculate quota reset date (monthly)
export function calculateQuotaResetDate(): Date {
const now = new Date();
const nextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);
return nextMonth;
}