新增NewJsiteServer.vue及aliyun_server_light.py
This commit is contained in:
parent
ebc9eb036c
commit
403ae1fe49
693
dashboard/src2/pages/NewJsiteServer.vue
Normal file
693
dashboard/src2/pages/NewJsiteServer.vue
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :options="{ title: '新建 Jsite 服务器', size: 'lg' }" v-model="show">
|
||||||
|
<!-- 第一步:选择服务器配置和支付方式 -->
|
||||||
|
<template v-if="!showPaymentProcessing" #body-content>
|
||||||
|
<div class="p-4 sm:p-6">
|
||||||
|
<div class="mb-6">
|
||||||
|
<p class="mt-1 text-sm text-gray-600">
|
||||||
|
请选择服务器配置并填写相关信息,点击"创建"后将自动为您开通。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<form @submit.prevent="createInstance">
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">地域选择</label>
|
||||||
|
<select v-model="selectedRegionId" class="w-full border rounded px-3 py-2" required @change="onRegionChange">
|
||||||
|
<option value="">请选择地域</option>
|
||||||
|
<option v-for="region in regions" :key="getRegionId(region)" :value="getRegionId(region)">
|
||||||
|
{{ getRegionName(region) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<!-- 调试信息 -->
|
||||||
|
<div v-if="regions.length === 0" class="mt-2 text-sm text-gray-500">
|
||||||
|
正在加载地域列表...
|
||||||
|
</div>
|
||||||
|
<div v-if="regions.length > 0" class="mt-2 text-sm text-gray-500">
|
||||||
|
已加载 {{ regions.length }} 个地域
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">套餐选择</label>
|
||||||
|
<select v-model="selectedPlanId" class="w-full border rounded px-3 py-2" required>
|
||||||
|
<option value="">请选择套餐</option>
|
||||||
|
<option v-for="plan in plans" :key="plan.plan_id" :value="plan.plan_id">
|
||||||
|
{{ getPlanDisplayName(plan) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">镜像选择</label>
|
||||||
|
<select v-model="selectedImageId" class="w-full border rounded px-3 py-2" required>
|
||||||
|
<option value="">请选择镜像</option>
|
||||||
|
<option v-for="image in images" :key="getImageId(image)" :value="getImageId(image)">
|
||||||
|
{{ getImageDisplayName(image) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">购买时长</label>
|
||||||
|
<select v-model="period" class="w-full border rounded px-3 py-2">
|
||||||
|
<option v-for="p in periods" :key="p.value" :value="p.value">{{ p.label }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 价格信息显示 -->
|
||||||
|
<div v-if="selectedPlanId && selectedImageId" class="mb-6 border-t border-gray-200 pt-4">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div class="text-sm text-gray-600">月度费用</div>
|
||||||
|
<div class="font-medium">
|
||||||
|
¥ {{ getSelectedPlanPrice() }}
|
||||||
|
<span class="text-gray-500 text-sm">(月付)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center mt-2">
|
||||||
|
<div class="text-sm text-gray-600">购买时长</div>
|
||||||
|
<div class="font-medium">{{ period }} 个月</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center mt-2 text-lg font-bold">
|
||||||
|
<div>总计</div>
|
||||||
|
<div>¥ {{ getTotalAmount() }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付方式选择 -->
|
||||||
|
<div class="mb-6 border-t border-gray-200 pt-4">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-3">
|
||||||
|
选择支付方式
|
||||||
|
</label>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-2">
|
||||||
|
<button
|
||||||
|
class="p-4 border rounded-lg flex flex-col items-center justify-center hover:bg-gray-50 transition-all"
|
||||||
|
:class="{'border-blue-500 border-2 shadow-sm': selectedPaymentMethod === 'balance', 'border-gray-200': selectedPaymentMethod !== 'balance'}"
|
||||||
|
@click="selectedPaymentMethod = 'balance'"
|
||||||
|
>
|
||||||
|
<span class="text-gray-800 font-medium">余额支付</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="p-4 border rounded-lg hover:bg-gray-50 transition-all"
|
||||||
|
:class="{'border-blue-500 border-2 shadow-sm': selectedPaymentMethod === 'alipay', 'border-gray-200': selectedPaymentMethod !== 'alipay'}"
|
||||||
|
@click="selectedPaymentMethod = 'alipay'"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<div class="mb-2">
|
||||||
|
<AlipayLogo class="h-8" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="p-4 border rounded-lg hover:bg-gray-50 transition-all"
|
||||||
|
:class="{'border-blue-500 border-2 shadow-sm': selectedPaymentMethod === 'wechatpay', 'border-gray-200': selectedPaymentMethod !== 'wechatpay'}"
|
||||||
|
@click="selectedPaymentMethod = 'wechatpay'"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<div class="mb-2">
|
||||||
|
<WeChatPayLogo class="h-8" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="error" class="mt-4 p-3 bg-red-50 text-red-700 rounded-md text-sm">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 第二步:处理支付 -->
|
||||||
|
<template v-else #body-content>
|
||||||
|
<div class="p-4 sm:p-6">
|
||||||
|
<!-- 加载中状态 -->
|
||||||
|
<div v-if="isProcessingPayment" class="text-center py-8">
|
||||||
|
<div class="h-12 w-12 mx-auto animate-spin text-blue-600 mb-4 flex items-center justify-center">
|
||||||
|
<i class="fe fe-loader text-3xl"></i>
|
||||||
|
</div>
|
||||||
|
<p class="text-gray-700 text-lg">正在处理支付,请稍候...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付成功状态 -->
|
||||||
|
<div v-else-if="paymentSuccess" class="text-center py-8">
|
||||||
|
<div class="flex justify-center mb-6">
|
||||||
|
<div class="rounded-full bg-green-100 p-3">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-medium text-gray-900 mb-2">服务器创建成功!</h3>
|
||||||
|
<p class="text-gray-600 mb-2">您的 Jsite 服务器已成功开通。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 微信支付状态 -->
|
||||||
|
<div v-else-if="selectedPaymentMethod === 'wechatpay' && paymentQrCode" class="text-center">
|
||||||
|
<div class="relative mb-6">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<WeChatPayLogo class="h-10" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mb-6">
|
||||||
|
<div class="text-sm text-gray-600 mb-2">扫一扫付款(元)</div>
|
||||||
|
<div class="text-3xl font-bold text-[#FF0036]">{{ order?.total_amount }} 元</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<div class="qrcode-container bg-white p-4 border border-gray-100 shadow-sm rounded-lg">
|
||||||
|
<img
|
||||||
|
:src="paymentQrCodeImage"
|
||||||
|
alt="微信支付二维码"
|
||||||
|
class="qrcode-image"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 text-center">
|
||||||
|
<p class="text-gray-600">
|
||||||
|
请使用微信扫描二维码完成支付
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-500 text-sm mt-2">二维码有效期 15 分钟</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付宝支付状态 -->
|
||||||
|
<div v-else-if="selectedPaymentMethod === 'alipay' && paymentUrl" class="text-center py-6">
|
||||||
|
<div class="mb-6">
|
||||||
|
<AlipayLogo class="h-10 mx-auto" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 mb-4">请在新页面完成支付宝支付</h3>
|
||||||
|
<p class="text-gray-600 mb-6">
|
||||||
|
如果没有自动跳转,请点击下方按钮打开支付页面
|
||||||
|
</p>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<button
|
||||||
|
class="w-full px-6 py-3 bg-[#1677FF] text-white rounded-lg hover:bg-[#0E5FD8] transition-colors shadow-sm"
|
||||||
|
@click="window.open(paymentUrl, '_blank')"
|
||||||
|
>
|
||||||
|
打开支付页面
|
||||||
|
</button>
|
||||||
|
<p class="text-gray-600 text-sm mt-4">
|
||||||
|
支付完成后,请稍等片刻,系统会自动刷新页面
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="error" class="mt-4 p-3 bg-red-50 text-red-700 rounded-md text-sm">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<div class="w-full">
|
||||||
|
<button v-if="!showPaymentProcessing && !paymentSuccess"
|
||||||
|
type="button"
|
||||||
|
class="w-full px-4 py-2 bg-[#1fc76f] border border-transparent rounded-md text-sm font-medium text-white hover:bg-[#19b862] focus:outline-none"
|
||||||
|
@click="createInstance"
|
||||||
|
:disabled="isLoading"
|
||||||
|
>
|
||||||
|
{{ isLoading ? '处理中...' : '确认创建' }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="flex justify-between w-full" v-else>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="px-4 py-2 bg-[#1fc76f] border border-transparent rounded-md text-sm font-medium text-white hover:bg-[#19b862] focus:outline-none ml-auto"
|
||||||
|
@click="close"
|
||||||
|
v-if="paymentSuccess"
|
||||||
|
>
|
||||||
|
关闭
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { toast } from 'vue-sonner';
|
||||||
|
import { Dialog } from 'jingrow-ui';
|
||||||
|
import { DashboardError } from '../utils/error';
|
||||||
|
import AlipayLogo from '../logo/AlipayLogo.vue';
|
||||||
|
import WeChatPayLogo from '../logo/WeChatPayLogo.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewJsiteServer',
|
||||||
|
components: {
|
||||||
|
Dialog,
|
||||||
|
AlipayLogo,
|
||||||
|
WeChatPayLogo
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: true,
|
||||||
|
selectedRegionId: '',
|
||||||
|
selectedPlanId: '',
|
||||||
|
selectedImageId: '',
|
||||||
|
period: 1,
|
||||||
|
selectedPaymentMethod: null, // 支付方式
|
||||||
|
regions: [],
|
||||||
|
plans: [],
|
||||||
|
images: [],
|
||||||
|
periods: [
|
||||||
|
{ value: 1, label: '1个月' },
|
||||||
|
{ value: 3, label: '3个月' },
|
||||||
|
{ value: 6, label: '6个月' },
|
||||||
|
{ value: 12, label: '1年' },
|
||||||
|
{ value: 24, label: '2年' },
|
||||||
|
{ value: 36, label: '3年' }
|
||||||
|
],
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
success: false,
|
||||||
|
// 支付相关状态
|
||||||
|
order: null,
|
||||||
|
showPaymentProcessing: false,
|
||||||
|
paymentSuccess: false,
|
||||||
|
isProcessingPayment: false,
|
||||||
|
paymentUrl: null,
|
||||||
|
paymentQrCode: null,
|
||||||
|
paymentQrCodeImage: null,
|
||||||
|
checkInterval: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
resources: {
|
||||||
|
aliyunRegions() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.aliyun_server_light.get_aliyun_regions',
|
||||||
|
onSuccess(response) {
|
||||||
|
console.log('地域API完整响应:', response);
|
||||||
|
|
||||||
|
// 后端返回格式: {success: true, data: {...}, message: '...'}
|
||||||
|
const data = response.data || response;
|
||||||
|
console.log('提取的data部分:', data);
|
||||||
|
|
||||||
|
// 处理不同的数据结构可能性
|
||||||
|
let regionsData = [];
|
||||||
|
|
||||||
|
if (data.regions) {
|
||||||
|
// 直接有regions字段
|
||||||
|
regionsData = data.regions;
|
||||||
|
} else if (data.Regions) {
|
||||||
|
// 阿里云API可能返回Regions字段
|
||||||
|
regionsData = data.Regions;
|
||||||
|
} else if (Array.isArray(data)) {
|
||||||
|
// 直接是数组
|
||||||
|
regionsData = data;
|
||||||
|
} else if (data.body && data.body.regions) {
|
||||||
|
// 嵌套在body中
|
||||||
|
regionsData = data.body.regions;
|
||||||
|
} else if (data.body && data.body.Regions) {
|
||||||
|
// 嵌套在body中,字段名大写
|
||||||
|
regionsData = data.body.Regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('解析后的地域数据:', regionsData);
|
||||||
|
|
||||||
|
if (Array.isArray(regionsData) && regionsData.length > 0) {
|
||||||
|
this.regions = regionsData;
|
||||||
|
console.log('成功设置地域数据:', this.regions);
|
||||||
|
} else {
|
||||||
|
this.error = '地域数据格式不正确或为空';
|
||||||
|
console.error('地域数据格式错误:', data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
console.error('获取地域失败:', error);
|
||||||
|
this.error = error.message || '获取地域失败';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
aliyunPlans() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.aliyun_server_light.get_aliyun_plans',
|
||||||
|
onSuccess(response) {
|
||||||
|
const data = response.data || response;
|
||||||
|
this.plans = data.plans || [];
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '获取套餐失败';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
aliyunImages() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.aliyun_server_light.get_aliyun_images',
|
||||||
|
onSuccess(response) {
|
||||||
|
console.log('镜像API完整响应:', response);
|
||||||
|
|
||||||
|
const data = response.data || response;
|
||||||
|
console.log('镜像API data部分:', data);
|
||||||
|
|
||||||
|
// 处理不同的数据结构可能性
|
||||||
|
let imagesData = [];
|
||||||
|
|
||||||
|
if (data.images) {
|
||||||
|
// 直接有images字段
|
||||||
|
imagesData = data.images;
|
||||||
|
} else if (data.Images) {
|
||||||
|
// 阿里云API可能返回Images字段
|
||||||
|
imagesData = data.Images;
|
||||||
|
} else if (Array.isArray(data)) {
|
||||||
|
// 直接是数组
|
||||||
|
imagesData = data;
|
||||||
|
} else if (data.body && data.body.images) {
|
||||||
|
// 嵌套在body中
|
||||||
|
imagesData = data.body.images;
|
||||||
|
} else if (data.body && data.body.Images) {
|
||||||
|
// 嵌套在body中,字段名大写
|
||||||
|
imagesData = data.body.Images;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('解析后的镜像数据:', imagesData);
|
||||||
|
|
||||||
|
if (Array.isArray(imagesData) && imagesData.length > 0) {
|
||||||
|
this.images = imagesData;
|
||||||
|
console.log('成功设置镜像数据:', this.images);
|
||||||
|
} else {
|
||||||
|
this.error = '镜像数据格式不正确或为空';
|
||||||
|
console.error('镜像数据格式错误:', data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
console.error('获取镜像失败:', error);
|
||||||
|
this.error = error.message || '获取镜像失败';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 创建服务器订单
|
||||||
|
createServerOrder() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.aliyun_server_light.create_server_order',
|
||||||
|
validate() {
|
||||||
|
if (!this.selectedRegionId || !this.selectedPlanId || !this.selectedImageId) {
|
||||||
|
throw new DashboardError('请选择完整配置');
|
||||||
|
}
|
||||||
|
if (!this.selectedPaymentMethod) {
|
||||||
|
throw new DashboardError('请选择支付方式');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(data) {
|
||||||
|
if (!data.success) {
|
||||||
|
this.error = data.message || '创建服务器订单失败';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('订单创建成功,完整响应:', data);
|
||||||
|
console.log('订单ID:', data.order?.order_id);
|
||||||
|
|
||||||
|
// 显示订单支付界面
|
||||||
|
this.order = data.order;
|
||||||
|
this.showPaymentProcessing = true;
|
||||||
|
|
||||||
|
// 立即处理支付
|
||||||
|
this.processPayment();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
console.error('创建服务器订单失败:', error);
|
||||||
|
this.error = error.message || '创建服务器订单失败';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 处理余额支付
|
||||||
|
processBalancePayment() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.billing.process_balance_payment_for_server_order',
|
||||||
|
validate() {
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
throw new DashboardError('缺少订单信息');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(response) {
|
||||||
|
if (response.status === "Error" || response.success === false) {
|
||||||
|
toast.error(response.message || '支付失败,请确保余额充足');
|
||||||
|
this.error = response.message || '余额不足';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderData = {
|
||||||
|
...this.order,
|
||||||
|
...(response.order || {}),
|
||||||
|
status: '已支付'
|
||||||
|
};
|
||||||
|
this.$emit('success', orderData);
|
||||||
|
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
this.paymentSuccess = true;
|
||||||
|
console.log('余额支付成功,更新状态:', {
|
||||||
|
paymentSuccess: this.paymentSuccess
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '余额支付处理失败';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 处理支付宝支付
|
||||||
|
processAlipayPayment() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.billing.process_alipay_server_order',
|
||||||
|
validate() {
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
throw new DashboardError('缺少订单信息');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(response) {
|
||||||
|
this.paymentUrl = response.payment_url;
|
||||||
|
window.open(response.payment_url, '_blank');
|
||||||
|
|
||||||
|
toast.success('支付页面已在新窗口打开');
|
||||||
|
|
||||||
|
this.startPaymentCheck();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '支付宝支付处理失败';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 处理微信支付
|
||||||
|
processWechatPayment() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.billing.process_wechatpay_server_order',
|
||||||
|
validate() {
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
throw new DashboardError('缺少订单信息');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(response) {
|
||||||
|
this.paymentQrCode = response.payment_url;
|
||||||
|
this.paymentQrCodeImage = response.qr_code_image || null;
|
||||||
|
|
||||||
|
this.startPaymentCheck();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '微信支付处理失败';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 检查支付状态
|
||||||
|
checkPaymentStatus() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.billing.check_server_order_payment_status',
|
||||||
|
validate() {
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
throw new DashboardError('缺少订单信息');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(data) {
|
||||||
|
if (data && data.status === '交易成功') {
|
||||||
|
this.stopPaymentCheck();
|
||||||
|
|
||||||
|
const orderData = { ...this.order, ...(data.order || {}) };
|
||||||
|
this.$emit('success', orderData);
|
||||||
|
|
||||||
|
this.paymentSuccess = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log('组件mounted,开始获取地域列表');
|
||||||
|
this.$resources.aliyunRegions.submit();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async onRegionChange() {
|
||||||
|
console.log('地域变更,选择的地域ID:', this.selectedRegionId);
|
||||||
|
|
||||||
|
this.selectedPlanId = '';
|
||||||
|
this.selectedImageId = '';
|
||||||
|
this.plans = [];
|
||||||
|
this.images = [];
|
||||||
|
|
||||||
|
if (this.selectedRegionId) {
|
||||||
|
console.log('开始获取套餐和镜像数据...');
|
||||||
|
await this.$resources.aliyunPlans.submit({ region_id: this.selectedRegionId });
|
||||||
|
await this.$resources.aliyunImages.submit({
|
||||||
|
region_id: this.selectedRegionId,
|
||||||
|
image_type: 'system' // 明确指定镜像类型
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async createInstance() {
|
||||||
|
if (!this.selectedRegionId || !this.selectedPlanId || !this.selectedImageId) {
|
||||||
|
this.error = '请选择完整配置';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.selectedPaymentMethod) {
|
||||||
|
this.error = '请选择支付方式';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.error = null;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
await this.$resources.createServerOrder.submit({
|
||||||
|
plan_id: this.selectedPlanId,
|
||||||
|
image_id: this.selectedImageId,
|
||||||
|
period: this.period,
|
||||||
|
region_id: this.selectedRegionId,
|
||||||
|
payment_method: this.selectedPaymentMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
processPayment() {
|
||||||
|
this.isProcessingPayment = true;
|
||||||
|
this.error = null;
|
||||||
|
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
console.error('缺少订单ID,无法继续支付');
|
||||||
|
this.error = '订单信息不完整,请重试';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('准备处理支付,订单信息:', {
|
||||||
|
id: this.order.order_id,
|
||||||
|
method: this.selectedPaymentMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.selectedPaymentMethod === 'balance') {
|
||||||
|
this.$resources.processBalancePayment.submit({
|
||||||
|
order_id: this.order.order_id
|
||||||
|
});
|
||||||
|
} else if (this.selectedPaymentMethod === 'alipay') {
|
||||||
|
this.$resources.processAlipayPayment.submit({
|
||||||
|
order_id: this.order.order_id
|
||||||
|
});
|
||||||
|
} else if (this.selectedPaymentMethod === 'wechatpay') {
|
||||||
|
this.$resources.processWechatPayment.submit({
|
||||||
|
order_id: this.order.order_id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startPaymentCheck() {
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
|
||||||
|
this.checkInterval = setInterval(() => {
|
||||||
|
this.$resources.checkPaymentStatus.submit();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
// 15分钟后停止检查
|
||||||
|
setTimeout(() => {
|
||||||
|
this.stopPaymentCheck();
|
||||||
|
}, 900000);
|
||||||
|
},
|
||||||
|
stopPaymentCheck() {
|
||||||
|
if (this.checkInterval) {
|
||||||
|
clearInterval(this.checkInterval);
|
||||||
|
this.checkInterval = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.stopPaymentCheck();
|
||||||
|
this.show = false;
|
||||||
|
this.$emit('success');
|
||||||
|
},
|
||||||
|
getRegionId(region) {
|
||||||
|
return region.region_id || region.RegionId || region.id;
|
||||||
|
},
|
||||||
|
getRegionName(region) {
|
||||||
|
return region.local_name || region.region_name || region.RegionName || region.name;
|
||||||
|
},
|
||||||
|
getPlanDisplayName(plan) {
|
||||||
|
// 根据阿里云API返回的数据结构显示套餐信息
|
||||||
|
const cpu = plan.core || plan.cpu || '未知';
|
||||||
|
const memory = plan.memory || '未知';
|
||||||
|
const disk = plan.disk_size || plan.system_disk_size || '未知';
|
||||||
|
const price = plan.origin_price || '未知';
|
||||||
|
// 处理内存显示(如果是小数,转换为MB)
|
||||||
|
let memoryDisplay = memory;
|
||||||
|
if (typeof memory === 'number' && memory < 1) {
|
||||||
|
memoryDisplay = Math.round(memory * 1024) + 'MB';
|
||||||
|
} else if (typeof memory === 'number') {
|
||||||
|
memoryDisplay = memory + 'GB';
|
||||||
|
}
|
||||||
|
return `${cpu}核/${memoryDisplay}/${disk}GB - ¥${price}/月`;
|
||||||
|
},
|
||||||
|
getSelectedPlanPrice() {
|
||||||
|
const selectedPlan = this.plans.find(plan => plan.plan_id === this.selectedPlanId);
|
||||||
|
return selectedPlan ? (selectedPlan.origin_price || 0) : 0;
|
||||||
|
},
|
||||||
|
getTotalAmount() {
|
||||||
|
const monthlyPrice = this.getSelectedPlanPrice();
|
||||||
|
return (monthlyPrice * this.period).toFixed(2);
|
||||||
|
},
|
||||||
|
getImageId(image) {
|
||||||
|
return image.image_id || image.ImageId || image.id;
|
||||||
|
},
|
||||||
|
getImageDisplayName(image) {
|
||||||
|
// 根据阿里云API返回的数据结构显示镜像信息
|
||||||
|
const name = image.image_name || image.name || image.ImageName || image.Name || '未知镜像';
|
||||||
|
const platform = image.platform || '';
|
||||||
|
const description = image.description || '';
|
||||||
|
|
||||||
|
// 显示格式:镜像名称 (平台)
|
||||||
|
if (platform) {
|
||||||
|
return `${name} (${platform})`;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.stopPaymentCheck();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.qrcode-container {
|
||||||
|
height: 250px;
|
||||||
|
width: 250px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-image {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加渐入动画 */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
376
jcloud/api/aliyun_server_light.py
Normal file
376
jcloud/api/aliyun_server_light.py
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
import jingrow
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from alibabacloud_swas_open20200601.client import Client as SWAS_OPEN20200601Client
|
||||||
|
from alibabacloud_credentials.client import Client as CredentialClient
|
||||||
|
from alibabacloud_tea_openapi import models as open_api_models
|
||||||
|
from alibabacloud_swas_open20200601 import models as swas__open20200601_models
|
||||||
|
from alibabacloud_tea_util import models as util_models
|
||||||
|
from alibabacloud_tea_util.client import Client as UtilClient
|
||||||
|
|
||||||
|
|
||||||
|
class AliyunLightServerManager:
|
||||||
|
"""阿里云轻量应用服务器管理器"""
|
||||||
|
_instance = None
|
||||||
|
_clients = {} # 缓存不同地域的客户端
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_instance(cls):
|
||||||
|
"""获取单例实例"""
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = cls()
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""初始化管理器"""
|
||||||
|
if AliyunLightServerManager._instance is not None:
|
||||||
|
raise Exception("请使用 AliyunLightServerManager.get_instance() 获取实例")
|
||||||
|
|
||||||
|
# 初始化配置
|
||||||
|
self._initialize_credentials()
|
||||||
|
|
||||||
|
def _initialize_credentials(self):
|
||||||
|
"""初始化阿里云凭据"""
|
||||||
|
try:
|
||||||
|
settings = jingrow.get_single("Jcloud Settings")
|
||||||
|
if not settings:
|
||||||
|
jingrow.log_error("阿里云凭据初始化失败", "无法获取Jcloud Settings配置")
|
||||||
|
raise Exception("Jcloud Settings配置不存在")
|
||||||
|
|
||||||
|
self.access_key_id = settings.get("aliyun_access_key_id")
|
||||||
|
self.access_secret = settings.get_password("aliyun_access_secret") if settings.get("aliyun_access_secret") else None
|
||||||
|
|
||||||
|
if not self.access_key_id or not self.access_secret:
|
||||||
|
jingrow.log_error("阿里云凭据初始化失败", "阿里云凭据未配置,请在Jcloud Settings中配置aliyun_access_key_id和aliyun_access_secret")
|
||||||
|
raise Exception("阿里云凭据未配置")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error(f"阿里云凭据初始化失败: {str(e)}")
|
||||||
|
self.access_key_id = None
|
||||||
|
self.access_secret = None
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _get_client(self, region_id: str = 'cn-shanghai') -> SWAS_OPEN20200601Client:
|
||||||
|
"""获取指定地域的客户端,使用缓存机制"""
|
||||||
|
if region_id not in self._clients:
|
||||||
|
try:
|
||||||
|
# 直接使用凭据创建配置
|
||||||
|
config = open_api_models.Config(
|
||||||
|
access_key_id=self.access_key_id,
|
||||||
|
access_key_secret=self.access_secret
|
||||||
|
)
|
||||||
|
config.endpoint = f'swas.{region_id}.aliyuncs.com'
|
||||||
|
self._clients[region_id] = SWAS_OPEN20200601Client(config)
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("阿里云客户端创建失败", f"创建客户端时发生错误: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
return self._clients[region_id]
|
||||||
|
|
||||||
|
def _convert_response_to_dict(self, response_body):
|
||||||
|
"""将阿里云SDK响应对象转换为可序列化的字典"""
|
||||||
|
if response_body is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
# 获取所有公共属性
|
||||||
|
for attr_name in dir(response_body):
|
||||||
|
if not attr_name.startswith('_') and not callable(getattr(response_body, attr_name)):
|
||||||
|
attr_value = getattr(response_body, attr_name)
|
||||||
|
if attr_value is not None:
|
||||||
|
if hasattr(attr_value, '__dict__') and not isinstance(attr_value, (str, int, float, bool)):
|
||||||
|
# 如果是复杂对象,递归转换
|
||||||
|
result[attr_name] = self._convert_response_to_dict(attr_value)
|
||||||
|
elif isinstance(attr_value, list):
|
||||||
|
# 如果是列表,转换列表中的每个元素
|
||||||
|
converted_list = []
|
||||||
|
for item in attr_value:
|
||||||
|
if hasattr(item, '__dict__') and not isinstance(item, (str, int, float, bool)):
|
||||||
|
converted_list.append(self._convert_response_to_dict(item))
|
||||||
|
else:
|
||||||
|
converted_list.append(item)
|
||||||
|
result[attr_name] = converted_list
|
||||||
|
else:
|
||||||
|
# 基本类型直接赋值
|
||||||
|
result[attr_name] = attr_value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def create_instance(self, plan_id, image_id, period=1, region_id='cn-shanghai'):
|
||||||
|
"""创建轻量应用服务器实例"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.CreateInstancesRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
plan_id=plan_id,
|
||||||
|
image_id=image_id,
|
||||||
|
period=period
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.create_instances_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例创建成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("创建实例失败", f"创建实例时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '实例创建失败'}
|
||||||
|
|
||||||
|
def start_instance(self, instance_id, region_id='cn-shanghai'):
|
||||||
|
"""启动实例"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.StartInstanceRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
instance_id=instance_id
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.start_instance_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例启动成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("启动实例失败", f"启动实例 {instance_id} 时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '实例启动失败'}
|
||||||
|
|
||||||
|
def stop_instance(self, instance_id, region_id='cn-shanghai'):
|
||||||
|
"""停止实例"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.StopInstanceRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
instance_id=instance_id
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.stop_instance_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例停止成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("停止实例失败", f"停止实例 {instance_id} 时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '实例停止失败'}
|
||||||
|
|
||||||
|
def upgrade_instance(self, instance_id, plan_id, region_id='cn-shanghai'):
|
||||||
|
"""升级实例配置"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.UpgradeInstanceRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
instance_id=instance_id,
|
||||||
|
plan_id=plan_id
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.upgrade_instance_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例升级成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("升级实例失败", f"升级实例 {instance_id} 时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '实例升级失败'}
|
||||||
|
|
||||||
|
def renew_instance(self, instance_id, period=1, period_unit='Month', region_id='cn-shanghai'):
|
||||||
|
"""续费实例"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.RenewInstanceRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
instance_id=instance_id,
|
||||||
|
period=period,
|
||||||
|
period_unit=period_unit
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.renew_instance_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例续费成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("续费实例失败", f"续费实例 {instance_id} 时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '实例续费失败'}
|
||||||
|
|
||||||
|
def reset_system(self, instance_id, image_id, password=None, region_id='cn-shanghai'):
|
||||||
|
"""重置系统"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.ResetSystemRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
instance_id=instance_id,
|
||||||
|
image_id=image_id
|
||||||
|
)
|
||||||
|
if password:
|
||||||
|
request.password = password
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.reset_system_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '系统重置成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("重置系统失败", f"重置实例 {instance_id} 系统时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '系统重置失败'}
|
||||||
|
|
||||||
|
def get_instance_info(self, instance_id, region_id='cn-shanghai'):
|
||||||
|
"""获取实例信息"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.GetInstanceRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
instance_id=instance_id
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.get_instance_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取实例信息成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("获取实例信息失败", f"获取实例 {instance_id} 信息时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '获取实例信息失败'}
|
||||||
|
|
||||||
|
def list_instances(self, page_number=1, page_size=20, region_id='cn-shanghai'):
|
||||||
|
"""获取实例列表"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.ListInstancesRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
page_number=page_number,
|
||||||
|
page_size=page_size
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.list_instances_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取实例列表成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("获取实例列表失败", f"获取实例列表时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '获取实例列表失败'}
|
||||||
|
|
||||||
|
def delete_instance(self, instance_id, region_id='cn-shanghai'):
|
||||||
|
"""删除实例"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.DeleteInstanceRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
instance_id=instance_id
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.delete_instance_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例删除成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("删除实例失败", f"删除实例 {instance_id} 时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '实例删除失败'}
|
||||||
|
|
||||||
|
def get_plans(self, region_id='cn-shanghai'):
|
||||||
|
"""获取可用套餐列表"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.ListPlansRequest(
|
||||||
|
region_id=region_id
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.list_plans_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取套餐列表成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("获取套餐列表失败", f"获取套餐列表时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '获取套餐列表失败'}
|
||||||
|
|
||||||
|
def get_images(self, image_type='system', region_id='cn-shanghai'):
|
||||||
|
"""获取可用镜像列表"""
|
||||||
|
client = self._get_client(region_id)
|
||||||
|
try:
|
||||||
|
request = swas__open20200601_models.ListImagesRequest(
|
||||||
|
region_id=region_id,
|
||||||
|
image_type=image_type
|
||||||
|
)
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.list_images_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取镜像列表成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("获取镜像列表失败", f"获取镜像列表时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '获取镜像列表失败'}
|
||||||
|
|
||||||
|
def get_regions(self):
|
||||||
|
"""获取可用地域列表"""
|
||||||
|
try:
|
||||||
|
# 直接使用凭据创建配置
|
||||||
|
config = open_api_models.Config(
|
||||||
|
access_key_id=self.access_key_id,
|
||||||
|
access_key_secret=self.access_secret
|
||||||
|
)
|
||||||
|
# 默认用上海endpoint,获取所有地域
|
||||||
|
config.endpoint = 'swas.cn-shanghai.aliyuncs.com'
|
||||||
|
client = SWAS_OPEN20200601Client(config)
|
||||||
|
request = swas__open20200601_models.ListRegionsRequest(accept_language='zh-CN')
|
||||||
|
runtime = util_models.RuntimeOptions()
|
||||||
|
response = client.list_regions_with_options(request, runtime)
|
||||||
|
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取地域列表成功'}
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("获取地域列表失败", f"获取地域列表时发生错误: {str(e)}")
|
||||||
|
return {'success': False, 'error': str(e), 'message': '获取地域列表失败'}
|
||||||
|
|
||||||
|
|
||||||
|
# 全局管理器实例
|
||||||
|
_aliyun_manager = None
|
||||||
|
|
||||||
|
def _get_manager():
|
||||||
|
"""获取阿里云管理器实例"""
|
||||||
|
global _aliyun_manager
|
||||||
|
if _aliyun_manager is None:
|
||||||
|
_aliyun_manager = AliyunLightServerManager.get_instance()
|
||||||
|
return _aliyun_manager
|
||||||
|
|
||||||
|
|
||||||
|
# API函数 - 使用@jingrow.whitelist()装饰器
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def create_aliyun_instance(plan_id, image_id, period=1, region_id='cn-shanghai'):
|
||||||
|
"""创建轻量应用服务器实例"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.create_instance(plan_id, image_id, period, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def start_aliyun_instance(instance_id, region_id='cn-shanghai'):
|
||||||
|
"""启动实例"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.start_instance(instance_id, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def stop_aliyun_instance(instance_id, region_id='cn-shanghai'):
|
||||||
|
"""停止实例"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.stop_instance(instance_id, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def upgrade_aliyun_instance(instance_id, plan_id, region_id='cn-shanghai'):
|
||||||
|
"""升级实例配置"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.upgrade_instance(instance_id, plan_id, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def renew_aliyun_instance(instance_id, period=1, period_unit='Month', region_id='cn-shanghai'):
|
||||||
|
"""续费实例"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.renew_instance(instance_id, period, period_unit, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def reset_aliyun_instance_system(instance_id, image_id, password=None, region_id='cn-shanghai'):
|
||||||
|
"""重置系统"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.reset_system(instance_id, image_id, password, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def get_aliyun_instance_info(instance_id, region_id='cn-shanghai'):
|
||||||
|
"""获取实例信息"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.get_instance_info(instance_id, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def list_aliyun_instances(page_number=1, page_size=20, region_id='cn-shanghai'):
|
||||||
|
"""获取实例列表"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.list_instances(page_number, page_size, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def delete_aliyun_instance(instance_id, region_id='cn-shanghai'):
|
||||||
|
"""删除实例"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.delete_instance(instance_id, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def get_aliyun_plans(region_id='cn-shanghai'):
|
||||||
|
"""获取可用套餐列表"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.get_plans(region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def get_aliyun_images(image_type='system', region_id='cn-shanghai'):
|
||||||
|
"""获取可用镜像列表"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.get_images(image_type, region_id)
|
||||||
|
|
||||||
|
@jingrow.whitelist()
|
||||||
|
def get_aliyun_regions():
|
||||||
|
"""通过阿里云SDK实时获取可用地域列表"""
|
||||||
|
manager = _get_manager()
|
||||||
|
return manager.get_regions()
|
||||||
Loading…
x
Reference in New Issue
Block a user