Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: cost quota and price #5014

Merged
merged 10 commits into from
Aug 30, 2024
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
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
Loading