Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,16 @@ jobs:
- name: Deploy to Vercel (Staging)
id: deploy-staging
run: |
DEPLOYMENT_URL=$(vercel deploy --token ${{ secrets.VERCEL_TOKEN }} --yes)
echo "🚀 Deploying to Vercel staging..."
DEPLOYMENT_URL=$(vercel deploy --token ${{ secrets.VERCEL_TOKEN }} --yes 2>&1 | grep -o 'https://[^[:space:]]*' | head -1)

if [ -z "$DEPLOYMENT_URL" ]; then
echo "❌ Failed to get deployment URL from Vercel output"
echo "Vercel output:"
vercel deploy --token ${{ secrets.VERCEL_TOKEN }} --yes
exit 1
fi

echo "deployment-url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT
echo "🚀 Staging deployment URL: $DEPLOYMENT_URL"
env:
Expand Down Expand Up @@ -453,7 +462,16 @@ jobs:
- name: Deploy to Vercel (Production)
id: deploy-production
run: |
DEPLOYMENT_URL=$(vercel deploy --prod --token ${{ secrets.VERCEL_TOKEN }} --yes)
echo "🚀 Deploying to Vercel production..."
DEPLOYMENT_URL=$(vercel deploy --prod --token ${{ secrets.VERCEL_TOKEN }} --yes 2>&1 | grep -o 'https://[^[:space:]]*' | head -1)

if [ -z "$DEPLOYMENT_URL" ]; then
echo "❌ Failed to get deployment URL from Vercel output"
echo "Vercel output:"
vercel deploy --prod --token ${{ secrets.VERCEL_TOKEN }} --yes
exit 1
fi

echo "deployment-url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT
echo "🚀 Production deployment URL: $DEPLOYMENT_URL"
env:
Expand Down Expand Up @@ -609,6 +627,10 @@ jobs:
if [ -z "$DEPLOYMENT_URL" ]; then
echo "❌ Deployment URL is empty, using fallback URL"
DEPLOYMENT_URL="${{ secrets.PRODUCTION_URL }}"
if [ -z "$DEPLOYMENT_URL" ]; then
echo "❌ No fallback URL available, using default"
DEPLOYMENT_URL="https://codeunia.com"
fi
fi

# Ensure URL has protocol
Expand Down Expand Up @@ -685,6 +707,9 @@ jobs:
DEPLOYMENT_URL="${{ needs.deploy-production.outputs.deployment-url }}"
if [ -z "$DEPLOYMENT_URL" ]; then
DEPLOYMENT_URL="${{ secrets.PRODUCTION_URL }}"
if [ -z "$DEPLOYMENT_URL" ]; then
DEPLOYMENT_URL="https://codeunia.com"
fi
fi

echo "Testing URL: $DEPLOYMENT_URL"
Expand Down Expand Up @@ -758,6 +783,10 @@ jobs:
if [ -z "$DEPLOYMENT_URL" ]; then
echo "❌ Staging deployment URL is empty, using fallback URL"
DEPLOYMENT_URL="${{ secrets.STAGING_URL }}"
if [ -z "$DEPLOYMENT_URL" ]; then
echo "❌ No fallback URL available, using default"
DEPLOYMENT_URL="https://codeunia.com"
fi
fi

# Ensure URL has protocol
Expand Down Expand Up @@ -828,6 +857,9 @@ jobs:
DEPLOYMENT_URL="${{ needs.deploy-staging.outputs.deployment-url }}"
if [ -z "$DEPLOYMENT_URL" ]; then
DEPLOYMENT_URL="${{ secrets.STAGING_URL }}"
if [ -z "$DEPLOYMENT_URL" ]; then
DEPLOYMENT_URL="https://codeunia.com"
fi
fi

echo "Testing URL: $DEPLOYMENT_URL"
Expand Down
2 changes: 1 addition & 1 deletion app/api/admin-page-views/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export async function GET() {
return NextResponse.json({ error: error.message }, { status: 500 });
}

const totalViews = (data || []).reduce((sum: number, blog: any) => sum + (blog.views || 0), 0);
const totalViews = (data || []).reduce((sum: number, blog: Record<string, unknown>) => sum + (blog.views as number || 0), 0);
return NextResponse.json({ totalViews });
}
6 changes: 3 additions & 3 deletions app/api/admin/tests/[id]/results/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ export async function GET(
const stats = {
totalRegistrations: registrations?.length || 0,
totalAttempts: allAttempts?.length || 0,
passedAttempts: allAttempts?.filter((a: any) => a.passed).length || 0,
averageScore: allAttempts ? allAttempts.reduce((sum: number, a: any) => sum + (a.score || 0), 0) / allAttempts.length : 0,
averageTime: allAttempts ? allAttempts.reduce((sum: number, a: any) => sum + (a.time_taken_minutes || 0), 0) / allAttempts.length : 0
passedAttempts: allAttempts?.filter((a: Record<string, unknown>) => a.passed).length || 0,
averageScore: allAttempts ? allAttempts.reduce((sum: number, a: Record<string, unknown>) => sum + (a.score as number || 0), 0) / allAttempts.length : 0,
averageTime: allAttempts ? allAttempts.reduce((sum: number, a: Record<string, unknown>) => sum + (a.time_taken_minutes as number || 0), 0) / allAttempts.length : 0
};

return NextResponse.json({
Expand Down
2 changes: 1 addition & 1 deletion app/api/admin/users/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const GET = withRateLimit(
throw new Error(`Failed to fetch users: ${error.message}`);
}

const users = profiles?.map((profile: any) => ({
const users = profiles?.map((profile: Record<string, unknown>) => ({
id: profile.id,
email: profile.email || '',
username: profile.username || '',
Expand Down
55 changes: 55 additions & 0 deletions app/api/analytics/performance/batch/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { NextRequest, NextResponse } from 'next/server'

export const runtime = 'nodejs'

export async function POST(request: NextRequest) {
try {
const body = await request.json()

// Validate request body
if (!body || !Array.isArray(body.metrics)) {
return NextResponse.json(
{ error: 'Invalid request body. Expected { metrics: [] }' },
{ status: 400 }
)
}

const { metrics } = body

// Process performance metrics
const processedMetrics = metrics.map((metric: Record<string, unknown>) => ({
...metric,
timestamp: new Date().toISOString(),
processed: true
}))

// Log metrics for monitoring
console.log(`📊 Performance metrics batch received: ${processedMetrics.length} metrics`)

// In a real implementation, you would:
// 1. Store metrics in database
// 2. Send to external monitoring service
// 3. Trigger alerts if thresholds exceeded

return NextResponse.json({
success: true,
processed: processedMetrics.length,
message: 'Performance metrics processed successfully'
})

} catch (error) {
console.error('Error processing performance metrics:', error)
return NextResponse.json(
{ error: 'Failed to process performance metrics' },
{ status: 500 }
)
}
}

export async function GET() {
return NextResponse.json({
message: 'Performance analytics endpoint is active',
methods: ['POST'],
status: 'healthy'
})
}
2 changes: 1 addition & 1 deletion app/api/internships/my-applications/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export async function GET() {
return NextResponse.json({ error: error.message }, { status: 500 })
}

const ids = (data || []).map((r: any) => r.internship_id)
const ids = (data || []).map((r: Record<string, unknown>) => r.internship_id)
const response = NextResponse.json({ appliedIds: ids })

// Prevent caching to ensure fresh data
Expand Down
2 changes: 1 addition & 1 deletion lib/monitoring/health-checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ export class HealthChecker {
message: `Database tables check failed: ${tablesError.message}`
});
} else {
const existingTables = tables?.map((t: any) => t.table_name) || [];
const existingTables = tables?.map((t: Record<string, unknown>) => t.table_name) || [];
const requiredTables = ['profiles', 'user_points', 'user_activity_log'];
const missingTables = requiredTables.filter(table => !existingTables.includes(table));

Expand Down
4 changes: 2 additions & 2 deletions lib/security/csp-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function getCSPConfig(request: NextRequest): CSPConfig {
"style-src 'self' 'nonce-" + nonce + "' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: https: blob:",
"connect-src 'self' https://*.supabase.co https://*.vercel.app wss://*.supabase.co https://api.razorpay.com",
"connect-src 'self' https://*.supabase.co https://*.vercel.app wss://*.supabase.co https://api.razorpay.com https://codeunia.com https://www.codeunia.com",
"frame-src 'self' https://checkout.razorpay.com",
"object-src 'none'",
"base-uri 'self'",
Expand Down Expand Up @@ -81,7 +81,7 @@ export function getDevelopmentCSP(): string {
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: https: blob:",
"connect-src 'self' https://*.supabase.co https://*.vercel.app wss://*.supabase.co https://api.razorpay.com",
"connect-src 'self' https://*.supabase.co https://*.vercel.app wss://*.supabase.co https://api.razorpay.com https://codeunia.com https://www.codeunia.com",
"frame-src 'self' https://checkout.razorpay.com",
"object-src 'none'",
"base-uri 'self'",
Expand Down
28 changes: 14 additions & 14 deletions lib/services/audit-logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,16 +167,16 @@ export class AuditLogger {
}

// Transform the data to include admin name and email
const logs = data?.map((log: any) => ({
id: log.id,
admin_id: log.admin_id,
action_type: log.action_type,
target_resource: log.target_resource,
target_id: log.target_id,
metadata: log.metadata,
ip_address: log.ip_address,
user_agent: log.user_agent,
created_at: log.created_at,
const logs = data?.map((log: Record<string, unknown>) => ({
id: log.id as string,
admin_id: log.admin_id as string,
action_type: log.action_type as AuditActionType,
target_resource: log.target_resource as string,
target_id: log.target_id as string | undefined,
metadata: log.metadata as Record<string, unknown>,
ip_address: log.ip_address as string | undefined,
user_agent: log.user_agent as string | undefined,
created_at: log.created_at as string,
admin_name: 'Admin User', // Will be populated when profiles table is available
admin_email: 'admin@codeunia.com' // Will be populated when profiles table is available
})) || [];
Expand Down Expand Up @@ -231,8 +231,8 @@ export class AuditLogger {
.gte('created_at', startDate.toISOString());

const actionsByTypeMap: Record<string, number> = {};
actionsByType?.forEach((action: any) => {
actionsByTypeMap[action.action_type] = (actionsByTypeMap[action.action_type] || 0) + 1;
actionsByType?.forEach((action: Record<string, unknown>) => {
actionsByTypeMap[action.action_type as string] = (actionsByTypeMap[action.action_type as string] || 0) + 1;
});

// Get actions by admin
Expand All @@ -242,8 +242,8 @@ export class AuditLogger {
.gte('created_at', startDate.toISOString());

const adminCounts: Record<string, { name: string; count: number }> = {};
actionsByAdmin?.forEach((action: any) => {
const adminId = action.admin_id;
actionsByAdmin?.forEach((action: Record<string, unknown>) => {
const adminId = action.admin_id as string;
const adminName = 'Admin User'; // Will be populated when profiles table is available

if (!adminCounts[adminId]) {
Expand Down
2 changes: 1 addition & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const nextConfig: NextConfig = {
return buildId
},

// Simplified webpack config for better Vercel compatibility
// Minimal webpack config for better Vercel compatibility
webpack: (config, { isServer }) => {
// Only add essential configurations
if (!isServer) {
Expand Down
Loading