mirror of
				https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
				synced 2025-11-04 16:23:41 +08:00 
			
		
		
		
	fix: #463 add subscrption total amount
This commit is contained in:
		@@ -96,26 +96,18 @@ export function Settings(props: { closeSettings: () => void }) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const [usage, setUsage] = useState<{
 | 
					  const [usage, setUsage] = useState<{
 | 
				
			||||||
    used?: number;
 | 
					    used?: number;
 | 
				
			||||||
 | 
					    subscription?: number;
 | 
				
			||||||
  }>();
 | 
					  }>();
 | 
				
			||||||
  const [loadingUsage, setLoadingUsage] = useState(false);
 | 
					  const [loadingUsage, setLoadingUsage] = useState(false);
 | 
				
			||||||
  function checkUsage() {
 | 
					  function checkUsage() {
 | 
				
			||||||
    setLoadingUsage(true);
 | 
					    setLoadingUsage(true);
 | 
				
			||||||
    requestUsage()
 | 
					    requestUsage()
 | 
				
			||||||
      .then((res) =>
 | 
					      .then((res) => setUsage(res))
 | 
				
			||||||
        setUsage({
 | 
					 | 
				
			||||||
          used: res,
 | 
					 | 
				
			||||||
        }),
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
      .finally(() => {
 | 
					      .finally(() => {
 | 
				
			||||||
        setLoadingUsage(false);
 | 
					        setLoadingUsage(false);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					 | 
				
			||||||
    checkUpdate();
 | 
					 | 
				
			||||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
					 | 
				
			||||||
  }, []);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const accessStore = useAccessStore();
 | 
					  const accessStore = useAccessStore();
 | 
				
			||||||
  const enabledAccessControl = useMemo(
 | 
					  const enabledAccessControl = useMemo(
 | 
				
			||||||
    () => accessStore.enabledAccessControl(),
 | 
					    () => accessStore.enabledAccessControl(),
 | 
				
			||||||
@@ -127,12 +119,13 @@ export function Settings(props: { closeSettings: () => void }) {
 | 
				
			|||||||
  const builtinCount = SearchService.count.builtin;
 | 
					  const builtinCount = SearchService.count.builtin;
 | 
				
			||||||
  const customCount = promptStore.prompts.size ?? 0;
 | 
					  const customCount = promptStore.prompts.size ?? 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const showUsage = accessStore.token !== "";
 | 
					  const showUsage = !!accessStore.token || !!accessStore.accessCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (showUsage) {
 | 
					    checkUpdate();
 | 
				
			||||||
      checkUsage();
 | 
					    showUsage && checkUsage();
 | 
				
			||||||
    }
 | 
					    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
				
			||||||
  }, [showUsage]);
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <ErrorBoundary>
 | 
					    <ErrorBoundary>
 | 
				
			||||||
@@ -392,7 +385,10 @@ export function Settings(props: { closeSettings: () => void }) {
 | 
				
			|||||||
              showUsage
 | 
					              showUsage
 | 
				
			||||||
                ? loadingUsage
 | 
					                ? loadingUsage
 | 
				
			||||||
                  ? Locale.Settings.Usage.IsChecking
 | 
					                  ? Locale.Settings.Usage.IsChecking
 | 
				
			||||||
                  : Locale.Settings.Usage.SubTitle(usage?.used ?? "[?]")
 | 
					                  : Locale.Settings.Usage.SubTitle(
 | 
				
			||||||
 | 
					                      usage?.used ?? "[?]",
 | 
				
			||||||
 | 
					                      usage?.subscription ?? "[?]",
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
                : Locale.Settings.Usage.NoAccess
 | 
					                : Locale.Settings.Usage.NoAccess
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ import { SubmitKey } from "../store/app";
 | 
				
			|||||||
const cn = {
 | 
					const cn = {
 | 
				
			||||||
  WIP: "该功能仍在开发中……",
 | 
					  WIP: "该功能仍在开发中……",
 | 
				
			||||||
  Error: {
 | 
					  Error: {
 | 
				
			||||||
    Unauthorized: "现在是未授权状态,请在设置页输入授权码。",
 | 
					    Unauthorized: "现在是未授权状态,请在设置页输入访问密码。",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  ChatItem: {
 | 
					  ChatItem: {
 | 
				
			||||||
    ChatItemCount: (count: number) => `${count} 条对话`,
 | 
					    ChatItemCount: (count: number) => `${count} 条对话`,
 | 
				
			||||||
@@ -104,22 +104,22 @@ const cn = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    Token: {
 | 
					    Token: {
 | 
				
			||||||
      Title: "API Key",
 | 
					      Title: "API Key",
 | 
				
			||||||
      SubTitle: "使用自己的 Key 可绕过授权访问限制",
 | 
					      SubTitle: "使用自己的 Key 可绕过密码访问限制",
 | 
				
			||||||
      Placeholder: "OpenAI API Key",
 | 
					      Placeholder: "OpenAI API Key",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    Usage: {
 | 
					    Usage: {
 | 
				
			||||||
      Title: "账户余额",
 | 
					      Title: "余额查询",
 | 
				
			||||||
      SubTitle(used: any) {
 | 
					      SubTitle(used: any, total: any) {
 | 
				
			||||||
        return `本月已使用 $${used}`;
 | 
					        return `本月已使用 $${used},订阅总额 $${total}`;
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      IsChecking: "正在检查…",
 | 
					      IsChecking: "正在检查…",
 | 
				
			||||||
      Check: "重新检查",
 | 
					      Check: "重新检查",
 | 
				
			||||||
      NoAccess: "输入API Key查看余额",
 | 
					      NoAccess: "输入 API Key 或访问密码查看余额",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    AccessCode: {
 | 
					    AccessCode: {
 | 
				
			||||||
      Title: "授权码",
 | 
					      Title: "访问密码",
 | 
				
			||||||
      SubTitle: "现在是未授权访问状态",
 | 
					      SubTitle: "现在是未授权访问状态",
 | 
				
			||||||
      Placeholder: "请输入授权码",
 | 
					      Placeholder: "请输入访问密码",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    Model: "模型 (model)",
 | 
					    Model: "模型 (model)",
 | 
				
			||||||
    Temperature: {
 | 
					    Temperature: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -112,8 +112,8 @@ const en: LocaleType = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    Usage: {
 | 
					    Usage: {
 | 
				
			||||||
      Title: "Account Balance",
 | 
					      Title: "Account Balance",
 | 
				
			||||||
      SubTitle(used: any) {
 | 
					      SubTitle(used: any, total: any) {
 | 
				
			||||||
        return `Used this month $${used}`;
 | 
					        return `Used this month $${used}, subscription $${total}`;
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      IsChecking: "Checking...",
 | 
					      IsChecking: "Checking...",
 | 
				
			||||||
      Check: "Check Again",
 | 
					      Check: "Check Again",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -112,8 +112,8 @@ const es: LocaleType = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    Usage: {
 | 
					    Usage: {
 | 
				
			||||||
      Title: "Saldo de la cuenta",
 | 
					      Title: "Saldo de la cuenta",
 | 
				
			||||||
      SubTitle(used: any) {
 | 
					      SubTitle(used: any, total: any) {
 | 
				
			||||||
        return `Usado $${used}`;
 | 
					        return `Usado $${used}, subscription $${total}`;
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      IsChecking: "Comprobando...",
 | 
					      IsChecking: "Comprobando...",
 | 
				
			||||||
      Check: "Comprobar de nuevo",
 | 
					      Check: "Comprobar de nuevo",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,8 +113,8 @@ const it: LocaleType = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    Usage: {
 | 
					    Usage: {
 | 
				
			||||||
      Title: "Bilancio Account",
 | 
					      Title: "Bilancio Account",
 | 
				
			||||||
      SubTitle(used: any) {
 | 
					      SubTitle(used: any, total: any) {
 | 
				
			||||||
        return `Usato in questo mese $${used}`;
 | 
					        return `Usato in questo mese $${used}, subscription $${total}`;
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      IsChecking: "Controllando...",
 | 
					      IsChecking: "Controllando...",
 | 
				
			||||||
      Check: "Controlla ancora",
 | 
					      Check: "Controlla ancora",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -109,8 +109,8 @@ const tw: LocaleType = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    Usage: {
 | 
					    Usage: {
 | 
				
			||||||
      Title: "帳戶餘額",
 | 
					      Title: "帳戶餘額",
 | 
				
			||||||
      SubTitle(used: any) {
 | 
					      SubTitle(used: any, total: any) {
 | 
				
			||||||
        return `本月已使用 $${used}`;
 | 
					        return `本月已使用 $${used},订阅总额 $${total}`;
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      IsChecking: "正在檢查…",
 | 
					      IsChecking: "正在檢查…",
 | 
				
			||||||
      Check: "重新檢查",
 | 
					      Check: "重新檢查",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
import type { ChatRequest, ChatReponse } from "./api/openai/typing";
 | 
					import type { ChatRequest, ChatReponse } from "./api/openai/typing";
 | 
				
			||||||
import { Message, ModelConfig, useAccessStore, useChatStore } from "./store";
 | 
					import { Message, ModelConfig, useAccessStore, useChatStore } from "./store";
 | 
				
			||||||
import Locale from "./locales";
 | 
					 | 
				
			||||||
import { showToast } from "./components/ui-lib";
 | 
					import { showToast } from "./components/ui-lib";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TIME_OUT_MS = 30000;
 | 
					const TIME_OUT_MS = 30000;
 | 
				
			||||||
@@ -83,31 +82,39 @@ export async function requestUsage() {
 | 
				
			|||||||
  const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
 | 
					  const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
 | 
				
			||||||
  const startDate = formatDate(startOfMonth);
 | 
					  const startDate = formatDate(startOfMonth);
 | 
				
			||||||
  const endDate = formatDate(now);
 | 
					  const endDate = formatDate(now);
 | 
				
			||||||
  const res = await requestOpenaiClient(
 | 
					 | 
				
			||||||
    `dashboard/billing/usage?start_date=${startDate}&end_date=${endDate}`,
 | 
					 | 
				
			||||||
  )(null, "GET");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  try {
 | 
					  const [used, subs] = await Promise.all([
 | 
				
			||||||
    const response = (await res.json()) as {
 | 
					    requestOpenaiClient(
 | 
				
			||||||
      total_usage: number;
 | 
					      `dashboard/billing/usage?start_date=${startDate}&end_date=${endDate}`,
 | 
				
			||||||
      error?: {
 | 
					    )(null, "GET"),
 | 
				
			||||||
        type: string;
 | 
					    requestOpenaiClient("dashboard/billing/subscription")(null, "GET"),
 | 
				
			||||||
        message: string;
 | 
					  ]);
 | 
				
			||||||
      };
 | 
					
 | 
				
			||||||
 | 
					  const response = (await used.json()) as {
 | 
				
			||||||
 | 
					    total_usage?: number;
 | 
				
			||||||
 | 
					    error?: {
 | 
				
			||||||
 | 
					      type: string;
 | 
				
			||||||
 | 
					      message: string;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (response.error && response.error.type) {
 | 
					  const total = (await subs.json()) as {
 | 
				
			||||||
      showToast(response.error.message);
 | 
					    hard_limit_usd?: number;
 | 
				
			||||||
      return;
 | 
					  };
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (response.total_usage) {
 | 
					  if (response.error && response.error.type) {
 | 
				
			||||||
      response.total_usage = Math.round(response.total_usage) / 100;
 | 
					    showToast(response.error.message);
 | 
				
			||||||
    }
 | 
					    return;
 | 
				
			||||||
    return response.total_usage;
 | 
					 | 
				
			||||||
  } catch (error) {
 | 
					 | 
				
			||||||
    console.error("[Request usage] ", error, res.body);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (response.total_usage) {
 | 
				
			||||||
 | 
					    response.total_usage = Math.round(response.total_usage) / 100;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    used: response.total_usage,
 | 
				
			||||||
 | 
					    subscription: total.hard_limit_usd,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function requestChatStream(
 | 
					export async function requestChatStream(
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user