token.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import { API } from './api';
  16. /**
  17. * 按需获取单个令牌的真实 key
  18. * @param {number|string} tokenId
  19. * @returns {Promise<string>} 返回不带 sk- 前缀的真实 token key
  20. */
  21. export async function fetchTokenKey(tokenId) {
  22. const response = await API.post(`/api/token/${tokenId}/key`);
  23. const { success, data, message } = response.data || {};
  24. if (!success || !data?.key) {
  25. throw new Error(message || 'Failed to fetch token key');
  26. }
  27. return data.key;
  28. }
  29. /**
  30. * 获取可用的 token keys
  31. * @returns {Promise<string[]>} 返回 active 状态的不带 sk- 前缀的真实 token key 数组
  32. */
  33. export async function fetchTokenKeys() {
  34. try {
  35. const response = await API.get('/api/token/?p=1&size=10');
  36. const { success, data } = response.data;
  37. if (!success) throw new Error('Failed to fetch token keys');
  38. const tokenItems = Array.isArray(data) ? data : data.items || [];
  39. const activeTokens = tokenItems.filter((token) => token.status === 1);
  40. const keyResults = await Promise.allSettled(
  41. activeTokens.map((token) => fetchTokenKey(token.id)),
  42. );
  43. return keyResults
  44. .filter((result) => result.status === 'fulfilled' && result.value)
  45. .map((result) => result.value);
  46. } catch (error) {
  47. console.error('Error fetching token keys:', error);
  48. return [];
  49. }
  50. }
  51. /**
  52. * 获取服务器地址
  53. * @returns {string} 服务器地址
  54. */
  55. export function getServerAddress() {
  56. let status = localStorage.getItem('status');
  57. let serverAddress = '';
  58. if (status) {
  59. try {
  60. status = JSON.parse(status);
  61. serverAddress = status.server_address || '';
  62. } catch (error) {
  63. console.error('Failed to parse status from localStorage:', error);
  64. }
  65. }
  66. if (!serverAddress) {
  67. serverAddress = window.location.origin;
  68. }
  69. return serverAddress;
  70. }
  71. export const CHANNEL_CONN_CLIPBOARD_TYPE = 'newapi_channel_conn';
  72. /**
  73. * @param {string} key - 完整的 API key(含 sk- 前缀)
  74. * @param {string} url - 服务器地址
  75. * @returns {string} JSON 格式的连接字符串
  76. */
  77. export function encodeChannelConnectionString(key, url) {
  78. return JSON.stringify({
  79. _type: CHANNEL_CONN_CLIPBOARD_TYPE,
  80. key,
  81. url,
  82. });
  83. }
  84. /**
  85. * @param {string} text - 剪贴板文本
  86. * @returns {{ key: string, url: string } | null}
  87. */
  88. export function parseChannelConnectionString(text) {
  89. if (!text || typeof text !== 'string') return null;
  90. try {
  91. const parsed = JSON.parse(text.trim());
  92. if (
  93. parsed &&
  94. typeof parsed === 'object' &&
  95. parsed._type === CHANNEL_CONN_CLIPBOARD_TYPE &&
  96. typeof parsed.key === 'string' &&
  97. typeof parsed.url === 'string'
  98. ) {
  99. return { key: parsed.key, url: parsed.url };
  100. }
  101. } catch {
  102. // not valid JSON
  103. }
  104. return null;
  105. }