ai-wpa/app/api/services/deploy-cloude/route.ts

199 lines
6.4 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { authMiddleware } from '@/lib/auth-middleware'
import connectDB from '@/lib/mongodb'
import { User as UserModel } from '@/models/user'
import BillingService from '@/lib/billing-service'
import { checkServiceAvailability } from '@/lib/system-settings'
// VPC subnet mapping based on datacenter
const VPC_SUBNET_MAP: { [key: string]: string } = {
inmumbaizone2: '3889f7ca-ca19-4851-abc7-5c6b4798b3fe', // Mumbai public subnet
inbangalore: 'c17032c9-3cfd-4028-8f2a-f3f5aa8c2976', // Bangalore public subnet
innoida: '95c60b59-4925-4b7e-bd05-afbf940d8000', // Delhi/Noida public subnet
defra1: '72ea201d-e1e7-4d30-a186-bc709efafad8', // Frankfurt public subnet
uslosangeles: '0e253cd1-8ecc-4b65-ae0f-6acbc53b0fb6', // Los Angeles public subnet
inbangalore3: 'f687a58b-04f0-4ebe-b583-65788a1d18bf', // Bangalore DC3 public subnet
}
export async function POST(request: NextRequest) {
try {
// Check if VPS deployment service is enabled
if (!(await checkServiceAvailability('vps'))) {
return NextResponse.json(
{
status: 'error',
message: 'VPS deployment service is currently disabled by administrator',
},
{ status: 503 }
)
}
// Check authentication using your auth middleware
const user = await authMiddleware(request)
if (!user) {
return NextResponse.json(
{ status: 'error', message: 'Unauthorized: Please login to continue' },
{ status: 401 }
)
}
// Get input data from request
const input = await request.json()
if (!input) {
return NextResponse.json({ status: 'error', message: 'Invalid JSON' }, { status: 400 })
}
// Get API key from environment
const UTHO_API_KEY = process.env.UTHO_API_KEY
if (!UTHO_API_KEY) {
return NextResponse.json(
{ status: 'error', message: 'API configuration missing' },
{ status: 500 }
)
}
// Connect to MongoDB
await connectDB()
// Get user data for billing
const userData = await UserModel.findOne({ email: user.email })
if (!userData) {
return NextResponse.json({ status: 'error', message: 'User not found' }, { status: 404 })
}
const requiredAmount = input.amount || 0
// Check balance only if amount > 0 (some services might be free)
if (requiredAmount > 0) {
const currentBalance = userData.balance || 0
if (currentBalance < requiredAmount) {
return NextResponse.json(
{
status: 'error',
message: `Insufficient balance. Required: ₹${requiredAmount}, Available: ₹${currentBalance}`,
code: 'INSUFFICIENT_BALANCE',
},
{ status: 400 }
)
}
}
// Get VPC subnet based on datacenter
const vpcSubnet = VPC_SUBNET_MAP[input.dclocation] || VPC_SUBNET_MAP['inbangalore']
// Prepare Utho payload - use the exact format from your PHP code
const uthoPayload = {
dcslug: input.dclocation,
planid: input.planid,
billingcycle: 'hourly',
auth: 'option2',
enable_publicip: input.publicip !== false,
subnetRequired: false,
firewall: '23434645',
cpumodel: 'intel',
enablebackup: input.backup || false,
root_password: input.password,
support: 'unmanaged',
vpc: vpcSubnet,
cloud: [
{
hostname: input.hostname,
},
],
image: input.image,
sshkeys: '',
}
console.log('Sending to Utho API:', uthoPayload)
// Make API request to Utho
const UTHO_API_URL = 'https://api.utho.com/v2/cloud/deploy'
const response = await fetch(UTHO_API_URL, {
method: 'POST',
headers: {
Authorization: `Bearer ${UTHO_API_KEY}`,
'Content-Type': 'application/json',
Accept: '*/*',
},
body: JSON.stringify(uthoPayload),
})
const httpCode = response.status
const deploymentSuccess = httpCode >= 200 && httpCode < 300
const responseData = await response.json()
// Add status field like PHP does
responseData.status = deploymentSuccess ? 'success' : 'error'
console.log('Utho API response:', { httpCode, responseData })
// Process billing using the new comprehensive billing service
try {
const billingResult = await BillingService.processServiceDeployment({
user: {
id: user.id,
email: user.email,
siliconId: userData.siliconId,
},
service: {
name: 'VPS Server',
type: 'vps',
id: responseData.server_id || responseData.id,
instanceId: responseData.server_id || responseData.id,
config: {
hostname: input.hostname,
planid: input.planid,
dclocation: input.dclocation,
image: input.image,
backup: input.backup,
publicip: input.publicip,
},
},
amount: requiredAmount,
currency: 'INR',
cycle: (input.cycle as any) || 'onetime',
deploymentSuccess,
deploymentResponse: responseData,
metadata: {
userAgent: request.headers.get('user-agent'),
ip: request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip'),
uthoPayload,
httpCode,
},
})
console.log('Billing processed:', {
billingId: billingResult.billing.billing_id,
transactionId: billingResult.transaction?.transactionId,
balanceUpdated: billingResult.balanceUpdated,
})
} catch (billingError) {
console.error('Billing processing failed:', billingError)
// Continue even if billing fails, but return error if it's balance-related
if (billingError instanceof Error && billingError.message.includes('Insufficient balance')) {
return NextResponse.json(
{
status: 'error',
message: billingError.message,
code: 'INSUFFICIENT_BALANCE',
},
{ status: 400 }
)
}
}
// Return the exact same response format as PHP
return NextResponse.json(responseData, { status: httpCode })
} catch (error) {
console.error('Cloud deployment error:', error)
return NextResponse.json(
{
status: 'error',
message: 'Internal server error',
details: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}