Skip to content

Commit

Permalink
feat: cost quota and price (labring#5014)
Browse files Browse the repository at this point in the history
* feat: init template cost quota and price

* feat: usage

* feat: price box

* fix: checkQuotaAllow

* fix: get quota

* fix: nodeport pricea

* fix: no used code

* fix: no used code

* fix: CheckQuotaType

* fix: cannot slice number
  • Loading branch information
zijiren233 authored and zjy365 committed Sep 3, 2024
1 parent 6fe30a7 commit d7a7179
Show file tree
Hide file tree
Showing 16 changed files with 739 additions and 11 deletions.
20 changes: 19 additions & 1 deletion frontend/providers/template/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
"Advanced Configuration": "Advanced",
"Age": "Uptime",
"AnticipatedPrice": "Estimated Cost",
"app": {
"Resource Quota": "Resource Quota",
"The applied CPU exceeds the quota": "Requested CPU exceeds your quota. Please contact admin.",
"The applied GPU exceeds the quota": "Requested GPU exceeds your quota. Please contact admin.",
"The applied memory exceeds the quota": "Requested storage exceeds your quota. Please contact admin.",
"The applied storage exceeds the quota": "The applied storage exceeds the quota"
},
"App Name": "App Name",
"Application Deployment": "App Deployment",
"Application List": " App List",
Expand All @@ -17,6 +24,7 @@
"Auto scaling": "Scaling",
"Basic Information": "Basic",
"Button Effect": "Button Appearance",
"cpu": "CPU",
"CPU": "CPU",
"CPU target is the CPU utilization rate of any container": "CPU target represents the CPU utilization rate of any container",
"CPU target value": "CPU Target",
Expand All @@ -25,6 +33,10 @@
"Command": "Command",
"Command parameters": "Command parameters",
"Component": "Component",
"common": {
"Surplus": "Surplus",
"Used": "Used"
},
"Config Form": "Form",
"ConfigMap Path Conflict": "ConfigMap Path Conflict",
"Configuration File": "Configmap",
Expand Down Expand Up @@ -67,6 +79,7 @@
"File Value can not empty": "File content is required",
"Filename can not empty": "File name is required",
"Fixed instance": "Fixed",
"gpu": "GPU",
"Heading to sealos soon": "Redirecting to Sealos shortly",
"Home Page": "Project Home",
"Html Part": "HTML Snippet",
Expand All @@ -89,13 +102,15 @@
"Log": "Log",
"Markdown Part": "Markdown Snippet",
"Max Storage Value": "Maximum storage: ",
"memory": "Memory",
"Memory": "Memory",
"Memory target value": "Memory Target",
"Min Storage Value": "Minimum storage: ",
"Mount Path Auth": "Mount path must match: /^[0-9a-zA-Z_/][0-9a-zA-Z_/.-]*[0-9a-zA-Z_/]$/",
"Name": "Name",
"Network Configuration": "Network",
"Next Execution Time": "Next Scheduled",
"Port": "Port",
"No Applications": "No Apps Available",
"None": "None",
"Not Configured": "Not Configured",
Expand Down Expand Up @@ -154,6 +169,7 @@
"Stateful": "Stateful",
"Stateless": "Stateless",
"Status": "Status",
"storage": "Storage",
"Storage": "Storage",
"Storage Range": "Storage Range",
"Storage Value can not empty": "Storage capacity is required",
Expand All @@ -165,6 +181,7 @@
"Templates": "Template Marketplace",
"Terminal": "Terminal",
"There is no resource of this type": "No resources of this type available",
"Total": "Total",
"TotalPrice": "Total Price",
"Type": "Type",
"Unload": "Uninstall",
Expand Down Expand Up @@ -213,5 +230,6 @@
"success": "success",
"target_value": "Target Value",
"users installed the app": "{{count}} users have installed this app",
"websocket": "WebSocket"
"websocket": "WebSocket",
"total_price_tip": "The estimated cost does not include port fees and traffic fees, and is subject to actual usage."
}
20 changes: 19 additions & 1 deletion frontend/providers/template/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
"Advanced Configuration": "高级配置",
"Age": "启动时长",
"AnticipatedPrice": "预估价格",
"app": {
"Resource Quota": "资源配额",
"The applied CPU exceeds the quota": "申请的 CPU 超出配额限制,请联系管理员",
"The applied GPU exceeds the quota": "申请的 GPU 超出配额限制,请联系管理员",
"The applied memory exceeds the quota": "申请的 '内存' 超出配额限制,请联系管理员",
"The applied storage exceeds the quota": "申请的 '存储' 超出配额限制,请联系管理员"
},
"App Name": "应用名称",
"Application Deployment": "应用部署",
"Application List": " 应用列表",
Expand All @@ -17,6 +24,7 @@
"Auto scaling": "弹性伸缩",
"Basic Information": "基本信息",
"Button Effect": "按钮效果",
"cpu": "CPU",
"CPU": "CPU",
"CPU target is the CPU utilization rate of any container": "CPU 目标值为任一容器的 CPU 利用率",
"CPU target value": "CPU 目标值",
Expand All @@ -25,6 +33,10 @@
"Command": "启动命令",
"Command parameters": "命令参数",
"Component": "组件",
"common": {
"Surplus": "剩余",
"Used": "已用"
},
"Config Form": "配置表单",
"ConfigMap Path Conflict": "配置文件路径冲突",
"Configuration File": "配置文件",
Expand Down Expand Up @@ -89,13 +101,15 @@
"Log": "日志",
"Markdown Part": "Markdown 片段",
"Max Storage Value": "容量最大为",
"memory": "内存",
"Memory": "内存",
"Memory target value": "内存目标值",
"Min Storage Value": "容量最小为",
"Mount Path Auth": "挂载路径需满足: /^[0-9a-zA-Z_/][0-9a-zA-Z_/.-]*[0-9a-zA-Z_/]$/",
"Name": "名字",
"Network Configuration": "网络配置",
"Next Execution Time": "下次执行时间",
"Port": "端口",
"No Applications": "暂无应用",
"None": "暂无",
"Not Configured": "未配置",
Expand Down Expand Up @@ -155,6 +169,7 @@
"Stateless": "无状态",
"Status": "状态",
"Storage": "存储卷",
"storage": "存储卷",
"Storage Range": "容量范围",
"Storage Value can not empty": "容量不能为空",
"Storage path can not empty": "挂载路径不能为空",
Expand All @@ -165,6 +180,7 @@
"Templates": "模板市场",
"Terminal": "终端",
"There is no resource of this type": "没有此类型的资源",
"Total": "总计",
"TotalPrice": "总价",
"Type": "类型",
"Unload": "卸载",
Expand Down Expand Up @@ -196,6 +212,7 @@
"file": "文件",
"file value": "文件值",
"filename": "文件名",
"gpu": "GPU",
"grpcs": "gRPCS",
"https": "HTTPS",
"jump_message": "该应用不允许单独使用,点击确认前往 Sealos Desktop 使用。",
Expand All @@ -213,5 +230,6 @@
"success": "成功",
"target_value": "目标值",
"users installed the app": "已有 {{count}} 名用户安装该应用",
"websocket": "WebSocket"
"websocket": "WebSocket",
"total_price_tip": "预估费用不包括端口费用和流量费用,以实际使用为准"
}
9 changes: 9 additions & 0 deletions frontend/providers/template/src/api/platform.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EnvResponse } from '@/types/index';
import { GET } from '@/services/request';
import { SystemConfigType, TemplateType } from '@/types/app';
import type { UserQuotaItemType, userPriceType } from '@/types/user';

export const updateRepo = () => GET('/api/updateRepo');

Expand All @@ -12,3 +13,11 @@ export const getPlatformEnv = () => GET<EnvResponse>('/api/platform/getEnv');
export const getSystemConfig = () => {
return GET<SystemConfigType>('/api/platform/getSystemConfig');
};

export const getUserQuota = () =>
GET<{
balance: number;
quota: UserQuotaItemType[];
}>('/api/platform/getQuota');

export const getResourcePrice = () => GET<userPriceType>('/api/platform/resourcePrice');
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion frontend/providers/template/src/components/Icon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ const map = {
empty: require('./icons/empty.svg').default,
dev: require('./icons/dev.svg').default,
eyeShow: require('./icons/eyeShow.svg').default,
tool: require('./icons/tool.svg').default
tool: require('./icons/tool.svg').default,
help: require('./icons/help.svg').default
};

const MyIcon = ({
Expand Down
3 changes: 3 additions & 0 deletions frontend/providers/template/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { EVENT_NAME } from 'sealos-desktop-sdk';
import { createSealosApp, sealosApp } from 'sealos-desktop-sdk/app';
import { useSystemConfigStore } from '@/store/config';
import useSessionStore from '@/store/session';
import { useUserStore } from '@/store/user';
import '@/styles/reset.scss';
import 'nprogress/nprogress.css';

Expand All @@ -41,9 +42,11 @@ const App = ({ Component, pageProps }: AppProps) => {
const { setScreenWidth, setLastRoute } = useGlobalStore();
const { initSystemConfig, initSystemEnvs } = useSystemConfigStore();
const [refresh, setRefresh] = useState(false);
const { loadUserSourcePrice } = useUserStore();

useEffect(() => {
initSystemConfig();
loadUserSourcePrice();
}, []);

useEffect(() => {
Expand Down
23 changes: 23 additions & 0 deletions frontend/providers/template/src/pages/api/platform/getQuota.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { getK8s } from '@/services/backend/kubernetes';
import { jsonRes } from '@/services/backend/response';
import { authSession } from '@/services/backend/auth';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
// source price
const { getUserQuota } = await getK8s({
kubeconfig: await authSession(req.headers)
});

const quota = await getUserQuota();

jsonRes(res, {
data: {
quota
}
});
} catch (error) {
jsonRes(res, { code: 500, message: 'get price error' });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { POST } from '@/services/request';
import { getK8s } from '@/services/backend/kubernetes';
import { jsonRes } from '@/services/backend/response';
import { authSession } from '@/services/backend/auth';
import type { userPriceType } from '@/types/user';

type properties = {
properties: property[];
};

type property = {
name: string;
unit_price: number;
unit: string;
};

export function transformProperties(data: properties): userPriceType {
const userPrice: userPriceType = {
cpu: 0,
memory: 0,
storage: 0,
nodeports: 0
};

data.properties.forEach((property: property) => {
switch (property.name) {
case 'cpu':
userPrice.cpu = property.unit_price;
break;
case 'memory':
userPrice.memory = property.unit_price;
break;
case 'storage':
userPrice.storage = property.unit_price;
break;
case 'services.nodeports':
userPrice.nodeports = property.unit_price;
break;
}
});

return userPrice;
}

const getResourcePrice = async () => {
const res = await fetch(
`https://account-api.${process.env.SEALOS_CLOUD_DOMAIN}/account/v1alpha1/properties`,
{
method: 'POST'
}
);
const data = await res.json();
return transformProperties(data.data as properties);
};

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
// source price
const { applyYamlList, k8sCustomObjects, k8sCore, namespace } = await getK8s({
kubeconfig: await authSession(req.headers)
});

const data = await getResourcePrice();

jsonRes<userPriceType>(res, {
code: 200,
data: data
});
} catch (error) {
console.log('get resoure price error: ', error);

jsonRes(res, { code: 500, message: 'get price error' });
}
}
Loading

0 comments on commit d7a7179

Please sign in to comment.