feat: establish NekoNest Cloud control and relay
This commit is contained in:
+149
@@ -0,0 +1,149 @@
|
||||
export type BillingPeriod = "month" | "year";
|
||||
|
||||
export function getPublicBetaPresentation(active: boolean) {
|
||||
return active
|
||||
? {
|
||||
status: "公测期间服务费全免",
|
||||
subline: "不绑支付方式 · 不会自动扣款",
|
||||
factValue: "¥0",
|
||||
factLabel: "公测服务费",
|
||||
priceHeading: "当前公测",
|
||||
priceValue: "¥0",
|
||||
priceDetail: "全部已允许主机",
|
||||
cardStatus: "当前有效",
|
||||
cardTitle: "公开公测",
|
||||
cardDescription: "公测期间,所有已经允许接入的主机槽位不收服务费。",
|
||||
cta: "进入公测控制台",
|
||||
comparison: "当前公测免费;未来方案未定",
|
||||
}
|
||||
: {
|
||||
status: "公测免费政策已结束",
|
||||
subline: "不会自动扣款 · 付费入口仍关闭",
|
||||
factValue: "关闭",
|
||||
factLabel: "公测免费",
|
||||
priceHeading: "公测政策",
|
||||
priceValue: "已结束",
|
||||
priceDetail: "不会转为自动扣款",
|
||||
cardStatus: "已经结束",
|
||||
cardTitle: "公开公测",
|
||||
cardDescription: "免费政策已结束;付费入口仍由上线门禁关闭,不会自动扣款。",
|
||||
cta: "进入控制台",
|
||||
comparison: "公测已结束;收费入口未开放",
|
||||
};
|
||||
}
|
||||
|
||||
export function getBillingEntitlementPresentation(
|
||||
mode: "public_beta" | "grant" | "none",
|
||||
publicBetaState: "open" | "gated" | "inactive" = "inactive",
|
||||
) {
|
||||
if (mode === "public_beta") {
|
||||
return {
|
||||
tone: "good" as const,
|
||||
status: "公测免费",
|
||||
title: "当前应付 ¥0",
|
||||
description: "不需要支付方式;公测结束不会自动生成付款或扣款。",
|
||||
emptyOrders: "免费公测不提供报价,也不会生成订单或付款单。",
|
||||
};
|
||||
}
|
||||
if (mode === "grant") {
|
||||
return {
|
||||
tone: "info" as const,
|
||||
status: "闭测邀请",
|
||||
title: "当前权益有效",
|
||||
description: "当前由有期限的非货币权益覆盖;不会生成付款或自动扣款。",
|
||||
emptyOrders: "闭测邀请不会生成报价、订单或付款单。",
|
||||
};
|
||||
}
|
||||
if (publicBetaState === "gated") {
|
||||
return {
|
||||
tone: "warn" as const,
|
||||
status: "公开接入冻结",
|
||||
title: "当前没有有效闭测邀请",
|
||||
description: "免费政策已经预设,但安全门禁尚未齐全;不会要求付款或自动创建订单。",
|
||||
emptyOrders: "公开接入冻结期间不会生成报价、订单或付款单。",
|
||||
};
|
||||
}
|
||||
return {
|
||||
tone: "neutral" as const,
|
||||
status: "无有效免费资格",
|
||||
title: "当前无免费权益",
|
||||
description: "公开公测当前未开放,该账户也没有有效闭测邀请;不会自动生成付款或扣款。",
|
||||
emptyOrders: "收费功能仍未开放,不会自动生成报价、订单或付款单。",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance a fixed-term order by one calendar billing period while clamping
|
||||
* month-end dates. This prevents January 31 from rolling into March and keeps
|
||||
* leap-day yearly orders on the final valid day of February.
|
||||
*/
|
||||
export function addBillingPeriod(
|
||||
startInput: Date | string,
|
||||
period: BillingPeriod,
|
||||
): string {
|
||||
const start = new Date(startInput);
|
||||
if (Number.isNaN(start.valueOf())) {
|
||||
throw new RangeError("Invalid billing term start");
|
||||
}
|
||||
|
||||
const requestedDay = start.getUTCDate();
|
||||
const result = new Date(start);
|
||||
result.setUTCDate(1);
|
||||
|
||||
if (period === "month") {
|
||||
result.setUTCMonth(result.getUTCMonth() + 1);
|
||||
} else {
|
||||
result.setUTCFullYear(result.getUTCFullYear() + 1);
|
||||
}
|
||||
|
||||
const lastDay = new Date(
|
||||
Date.UTC(result.getUTCFullYear(), result.getUTCMonth() + 1, 0),
|
||||
).getUTCDate();
|
||||
result.setUTCDate(Math.min(requestedDay, lastDay));
|
||||
return result.toISOString();
|
||||
}
|
||||
|
||||
/** Return the next time the currently summarized entitlement can change. */
|
||||
export function nextEntitlementExpiry(
|
||||
values: Array<string | null>,
|
||||
): string | null {
|
||||
return values
|
||||
.filter((value): value is string => Boolean(value))
|
||||
.sort()
|
||||
.at(0) ?? null;
|
||||
}
|
||||
|
||||
export type EntitlementComponent = {
|
||||
source: string;
|
||||
capacity: number | null;
|
||||
endsAt: string | null;
|
||||
};
|
||||
|
||||
export function summarizeEntitlementComponents(
|
||||
components: EntitlementComponent[],
|
||||
activeSlots: number,
|
||||
reservedSlots: number,
|
||||
) {
|
||||
const unlimitedComponents = components.filter(
|
||||
(component) => component.capacity === null,
|
||||
);
|
||||
const unlimited = unlimitedComponents.length > 0;
|
||||
const capacitySlots = unlimited
|
||||
? null
|
||||
: components.reduce((sum, component) => sum + (component.capacity ?? 0), 0);
|
||||
const effectiveUntil = unlimited
|
||||
? unlimitedComponents.some((component) => component.endsAt === null)
|
||||
? null
|
||||
: nextEntitlementExpiry(unlimitedComponents.map((component) => component.endsAt))
|
||||
: nextEntitlementExpiry(components.map((component) => component.endsAt));
|
||||
|
||||
return {
|
||||
unlimited,
|
||||
capacitySlots,
|
||||
availableSlots: unlimited
|
||||
? null
|
||||
: Math.max(0, (capacitySlots ?? 0) - activeSlots - reservedSlots),
|
||||
effectiveUntil,
|
||||
sources: [...new Set(components.map((component) => component.source))],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user