main
Some checks failed
Backend Deploy 🚀 / build-and-deploy (push) Has been cancelled

This commit is contained in:
Harun CAN
2026-03-30 15:18:20 +03:00
parent acb103657b
commit 013b2856bc
3 changed files with 159 additions and 22 deletions

View File

@@ -3,7 +3,7 @@ NODE_ENV=development
PORT=3000 PORT=3000
# Database # Database
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/boilerplate_db?schema=public" DATABASE_URL="postgresql://postgres:postgres@localhost:5432/contentgen_db?schema=public"
# JWT # JWT
JWT_SECRET=your-super-secret-jwt-key-change-in-production JWT_SECRET=your-super-secret-jwt-key-change-in-production
@@ -16,46 +16,85 @@ REDIS_PORT=6379
REDIS_PASSWORD= REDIS_PASSWORD=
# i18n # i18n
DEFAULT_LANGUAGE=en DEFAULT_LANGUAGE=tr
FALLBACK_LANGUAGE=en FALLBACK_LANGUAGE=en
# Frontend URL (CORS ve WebSocket için)
FRONTEND_URL=http://localhost:3001
# Optional Features (set to "true" to enable) # Optional Features (set to "true" to enable)
ENABLE_MAIL=false ENABLE_MAIL=false
ENABLE_S3=false ENABLE_S3=true
ENABLE_WEBSOCKET=false ENABLE_WEBSOCKET=true
ENABLE_MULTI_TENANCY=false ENABLE_MULTI_TENANCY=false
ENABLE_GEMINI=true
# Mail (Optional - only needed if ENABLE_MAIL=true) # Mail (Optional - only needed if ENABLE_MAIL=true)
MAIL_HOST=smtp.example.com MAIL_HOST=smtp.example.com
MAIL_PORT=587 MAIL_PORT=587
MAIL_USER= MAIL_USER=
MAIL_PASSWORD= MAIL_PASSWORD=
MAIL_FROM=noreply@example.com MAIL_FROM=noreply@contentgen.ai
# S3/MinIO (Optional - only needed if ENABLE_S3=true) # Cloudflare R2 / S3 Storage (ENABLE_S3=true gerekir)
S3_ENDPOINT=http://localhost:9000 # Cloudflare R2: https://dash.cloudflare.com → R2 → API Tokens
S3_ACCESS_KEY=minioadmin S3_ENDPOINT=https://<account_id>.r2.cloudflarestorage.com
S3_SECRET_KEY=minioadmin S3_ACCESS_KEY=your-r2-access-key
S3_BUCKET=uploads S3_SECRET_KEY=your-r2-secret-key
S3_REGION=us-east-1 S3_BUCKET=contentgen-media
S3_REGION=auto
S3_PUBLIC_URL=https://pub-xxxx.r2.dev
# Throttle / Rate Limiting # Throttle / Rate Limiting
THROTTLE_TTL=60000 THROTTLE_TTL=60000
THROTTLE_LIMIT=100 THROTTLE_LIMIT=100
# Gemini AI (Optional - only needed if ENABLE_GEMINI=true) # ─── AI API Keys ────────────────────────────────────────────────────
ENABLE_GEMINI=false
GOOGLE_API_KEY=your-google-api-key # Google Gemini AI — Script üretimi (ZORUNLU)
# https://aistudio.google.com/apikey
GOOGLE_API_KEY=your-google-gemini-api-key
GEMINI_MODEL=gemini-2.5-flash GEMINI_MODEL=gemini-2.5-flash
# AudioCraft — HuggingFace Inference API (MusicGen + AudioGen) # HiggsField AI — Video clip üretimi (ZORUNLU)
# MusicGen: Text-to-music üretimi # https://higgsfield.ai → Dashboard → API Keys
# AudioGen: Text-to-sound efekti üretimi HIGGSFIELD_API_KEY=your-higgsfield-api-key
# Ücretsiz HuggingFace hesabı yeterli: https://huggingface.co/settings/tokens HIGGSFIELD_BASE_URL=https://api.higgsfield.ai/v1
# ElevenLabs — Text-to-Speech narrasyon (ZORUNLU)
# https://elevenlabs.io → Profile → API Key
ELEVENLABS_API_KEY=your-elevenlabs-api-key
ELEVENLABS_VOICE_ID=21m00Tcm4TlvDq8ikWAM
ELEVENLABS_MODEL_ID=eleven_multilingual_v2
# Suno AI — Müzik üretimi (ZORUNLU)
# https://suno.com → API erişimi için iletişime geçin
SUNO_API_KEY=your-suno-api-key
SUNO_BASE_URL=https://api.suno.ai/v1
# AudioCraft — HuggingFace (Ambient ses, opsiyonel)
# https://huggingface.co/settings/tokens
HUGGINGFACE_API_KEY=hf_your-huggingface-api-key HUGGINGFACE_API_KEY=hf_your-huggingface-api-key
MUSICGEN_MODEL=facebook/musicgen-small MUSICGEN_MODEL=facebook/musicgen-small
AUDIOGEN_MODEL=facebook/audiogen-medium AUDIOGEN_MODEL=facebook/audiogen-medium
# Stripe Billing # X / Twitter API — Tweet → Video (ZORUNLU)
STRIPE_SECRET_KEY=sk_test_your-stripe-key # https://developer.twitter.com → Project → Keys and Tokens
STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secret TWITTER_BEARER_TOKEN=your-twitter-bearer-token
TWITTER_API_KEY=your-twitter-api-key
TWITTER_API_SECRET=your-twitter-api-secret
# ─── C# Media Worker ────────────────────────────────────────────────
# Worker'ın render tamamlandığında çağırdığı callback URL
# Local: http://localhost:3000/api/render-callback
# Production: https://api.contentgen.ai/api/render-callback
WORKER_CALLBACK_URL=http://localhost:3000/api/render-callback
# Redis queue name (Worker ile aynı olmalı)
VIDEO_QUEUE_NAME=video-generation
# ─── Stripe (Şimdilik devre dışı) ───────────────────────────────────
# Stripe entegrasyonu ilerleyen aşamada eklenecek
# STRIPE_SECRET_KEY=sk_test_your-stripe-key
# STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secret

View File

@@ -316,9 +316,68 @@ export class AdminController {
await this.prisma.rolePermission.deleteMany({ await this.prisma.rolePermission.deleteMany({
where: { roleId, permissionId }, where: { roleId, permissionId },
}); });
// Invalidate roles_list because permissions are nested in roles
await this.cacheManager.del('roles_list'); await this.cacheManager.del('roles_list');
return createSuccessResponse(null, 'Permission removed from role'); return createSuccessResponse(null, 'Permission removed from role');
} }
// ================== Project Management (Admin) ==================
@Get('projects')
@ApiOperation({ summary: 'Tüm projeleri getir (admin)' })
async getAllProjects(
@Query() query: { page?: number; limit?: number; status?: string; userId?: string },
): Promise<ApiResponse<any>> {
const result = await this.adminService.getAllProjects({
page: query.page ? Number(query.page) : 1,
limit: query.limit ? Number(query.limit) : 20,
status: query.status,
userId: query.userId,
});
return createSuccessResponse(result);
}
@Delete('projects/:id')
@ApiOperation({ summary: 'Projeyi sil (soft delete)' })
async adminDeleteProject(@Param('id') id: string): Promise<ApiResponse<any>> {
const result = await this.adminService.adminDeleteProject(id);
return createSuccessResponse(result, 'Proje silindi');
}
// ================== Render Job Management (Admin) ==================
@Get('render-jobs')
@ApiOperation({ summary: 'Tüm render jobları getir (admin)' })
async getAllRenderJobs(
@Query() query: { page?: number; limit?: number; status?: string },
): Promise<ApiResponse<any>> {
const result = await this.adminService.getAllRenderJobs({
page: query.page ? Number(query.page) : 1,
limit: query.limit ? Number(query.limit) : 20,
status: query.status,
});
return createSuccessResponse(result);
}
// ================== Ban / Activate User ==================
@Put('users/:id/ban')
@ApiOperation({ summary: 'Kullanıcıyı banla' })
async banUser(@Param('id') id: string): Promise<ApiResponse<any>> {
const user = await this.adminService.setUserActive(id, false);
return createSuccessResponse(
plainToInstance(UserResponseDto, user),
'Kullanıcı banlandı',
);
}
@Put('users/:id/activate')
@ApiOperation({ summary: 'Kullanıcıyı aktif et' })
async activateUser(@Param('id') id: string): Promise<ApiResponse<any>> {
const user = await this.adminService.setUserActive(id, true);
return createSuccessResponse(
plainToInstance(UserResponseDto, user),
'Kullanıcı aktif edildi',
);
}
} }

View File

@@ -22,6 +22,8 @@ export class AdminService {
storageStats, storageStats,
recentUsers, recentUsers,
projectsByStatus, projectsByStatus,
totalRenderJobs,
renderJobsByStatus,
] = await Promise.all([ ] = await Promise.all([
this.prisma.user.count(), this.prisma.user.count(),
this.prisma.user.count({ where: { isActive: true } }), this.prisma.user.count({ where: { isActive: true } }),
@@ -37,6 +39,11 @@ export class AdminService {
by: ['status'], by: ['status'],
_count: { id: true }, _count: { id: true },
}), }),
this.prisma.renderJob.count(),
this.prisma.renderJob.groupBy({
by: ['status'],
_count: { id: true },
}),
]); ]);
// Kredi istatistikleri // Kredi istatistikleri
@@ -63,6 +70,13 @@ export class AdminService {
return acc; return acc;
}, {} as Record<string, number>), }, {} as Record<string, number>),
}, },
renderJobs: {
total: totalRenderJobs,
byStatus: renderJobsByStatus.reduce((acc, item) => {
acc[item.status] = item._count.id;
return acc;
}, {} as Record<string, number>),
},
credits: { credits: {
totalGranted: creditStats._sum.amount || 0, totalGranted: creditStats._sum.amount || 0,
totalUsed: Math.abs(creditUsed._sum.amount || 0), totalUsed: Math.abs(creditUsed._sum.amount || 0),
@@ -106,6 +120,31 @@ export class AdminService {
}); });
} }
// ── Proje ve Render Yönetimi ──────────────────────────────────────
async getAllProjects() {
return this.prisma.project.findMany({
include: { user: { select: { email: true, firstName: true, lastName: true } } },
orderBy: { createdAt: 'desc' },
});
}
async getAllRenderJobs() {
return this.prisma.renderJob.findMany({
include: { project: { select: { name: true } } },
orderBy: { createdAt: 'desc' },
});
}
// ── Kullanıcı Yönetimi ────────────────────────────────────────────
async banUser(userId: string, isBanned: boolean) {
return this.prisma.user.update({
where: { id: userId },
data: { isActive: !isBanned },
});
}
// ── Kullanıcı Kredi Yönetimi ────────────────────────────────────── // ── Kullanıcı Kredi Yönetimi ──────────────────────────────────────
async grantCredits(userId: string, amount: number, description: string) { async grantCredits(userId: string, amount: number, description: string) {