Skip to content
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
10 changes: 0 additions & 10 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
module.exports = {
root: true,
extends: ['@antfu'],
rules: {
'antfu/top-level-function': 'off',
'@typescript-eslint/consistent-type-imports': 'off',
'@stylistic/ts/member-delimiter-style': 'off',
'@stylistic/ts/indent': 'off',
'@stylistic/js/no-tabs': 'off',
'vue/no-unused-refs': 'off',
'import/newline-after-import': 'off',
'unicorn/prefer-number-properties': 'off',
},
ignorePatterns: [
'vite.config.ts',
'tsconfig.json',
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ coverage

# Environment variables files
/service/.env
/docker-compose/nginx/html
/docker-compose/nginx/html
106 changes: 0 additions & 106 deletions service/src/chatgpt/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as console from 'node:console'
import * as dotenv from 'dotenv'
import 'isomorphic-fetch'
import type { ChatGPTAPIOptions, ChatMessage, SendMessageOptions } from 'chatgpt'
Expand All @@ -7,7 +6,6 @@ import { SocksProxyAgent } from 'socks-proxy-agent'
import httpsProxyAgent from 'https-proxy-agent'
import fetch from 'node-fetch'
import jwt_decode from 'jwt-decode'
import dayjs from 'dayjs'
import type { AuditConfig, KeyConfig, UserInfo } from '../storage/model'
import { Status } from '../storage/model'
import { convertImageUrl } from '../utils/image'
Expand Down Expand Up @@ -241,112 +239,8 @@ async function containsSensitiveWords(audit: AuditConfig, text: string): Promise
return false
}

async function fetchAccessTokenExpiredTime() {
const config = await getCacheConfig()
const jwt = jwt_decode(config.accessToken) as JWT
if (jwt.exp)
return dayjs.unix(jwt.exp).format('YYYY-MM-DD HH:mm:ss')
return '-'
}

let cachedBalance: number | undefined
let cacheExpiration = 0

async function fetchBalance() {
const now = new Date().getTime()
if (cachedBalance && cacheExpiration > now)
return Promise.resolve(cachedBalance.toFixed(3))

// 计算起始日期和结束日期
const startDate = new Date(now - 90 * 24 * 60 * 60 * 1000)
const endDate = new Date(now + 24 * 60 * 60 * 1000)

const config = await getCacheConfig()
const OPENAI_API_KEY = config.apiKey
const OPENAI_API_BASE_URL = config.apiBaseUrl

if (!isNotEmptyString(OPENAI_API_KEY))
return Promise.resolve('-')

const API_BASE_URL = isNotEmptyString(OPENAI_API_BASE_URL)
? OPENAI_API_BASE_URL
: 'https://api.openai.com'

// 查是否订阅
const urlSubscription = `${API_BASE_URL}/v1/dashboard/billing/subscription`
// 查普通账单
// const urlBalance = `${API_BASE_URL}/dashboard/billing/credit_grants`
// 查使用量
const urlUsage = `${API_BASE_URL}/v1/dashboard/billing/usage?start_date=${formatDate(startDate)}&end_date=${formatDate(endDate)}`

const headers = {
'Authorization': `Bearer ${OPENAI_API_KEY}`,
'Content-Type': 'application/json',
}
let socksAgent
let httpsAgent
if (isNotEmptyString(config.socksProxy)) {
socksAgent = new SocksProxyAgent({
hostname: config.socksProxy.split(':')[0],
port: Number.parseInt(config.socksProxy.split(':')[1]),
userId: isNotEmptyString(config.socksAuth) ? config.socksAuth.split(':')[0] : undefined,
password: isNotEmptyString(config.socksAuth) ? config.socksAuth.split(':')[1] : undefined,
})
}
else if (isNotEmptyString(config.httpsProxy)) {
httpsAgent = new HttpsProxyAgent(config.httpsProxy)
}

try {
// 获取API限额
let response = await fetch(urlSubscription, { agent: socksAgent === undefined ? httpsAgent : socksAgent, headers })
if (!response.ok) {
console.error('您的账户已被封禁,请登录OpenAI进行查看。')
return
}
interface SubscriptionData {
hard_limit_usd?: number
// 这里可以添加其他可能的属性
}
const subscriptionData: SubscriptionData = await response.json()
const totalAmount = subscriptionData.hard_limit_usd

interface UsageData {
total_usage?: number
// 这里可以添加其他可能的属性
}

// 获取已使用量
response = await fetch(urlUsage, { agent: socksAgent === undefined ? httpsAgent : socksAgent, headers })
const usageData: UsageData = await response.json()
const totalUsage = usageData.total_usage / 100

// 计算剩余额度
cachedBalance = totalAmount - totalUsage
cacheExpiration = now + 60 * 60 * 1000

return Promise.resolve(cachedBalance.toFixed(3))
}
catch (error) {
globalThis.console.error(error)
return Promise.resolve('-')
}
}

function formatDate(date) {
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')

return `${year}-${month}-${day}`
}

async function chatConfig() {
const config = await getOriginConfig() as ModelConfig
// if (config.apiModel === 'ChatGPTAPI')
// config.balance = await fetchBalance()
// else
// config.accessTokenExpiredTime = await fetchAccessTokenExpiredTime()
return sendResponse<ModelConfig>({
type: 'Success',
data: config,
Expand Down
22 changes: 11 additions & 11 deletions src/components/common/PromptStore/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const modalMode = ref('')
const tempModifiedItem = ref<any>({})

// 添加修改导入都使用一个Modal, 临时修改内容占用tempPromptKey,切换状态前先将内容都清楚
const changeShowModal = (mode: 'add' | 'modify' | 'local_import', selected = { key: '', value: '' }) => {
function changeShowModal(mode: 'add' | 'modify' | 'local_import', selected = { key: '', value: '' }) {
if (mode === 'add') {
tempPromptKey.value = ''
tempPromptValue.value = ''
Expand All @@ -82,15 +82,15 @@ const changeShowModal = (mode: 'add' | 'modify' | 'local_import', selected = { k
// 在线导入相关
const downloadURL = ref('')
const downloadDisabled = computed(() => downloadURL.value.trim().length < 1)
const setDownloadURL = (url: string) => {
function setDownloadURL(url: string) {
downloadURL.value = url
}

// 控制 input 按钮
const inputStatus = computed (() => tempPromptKey.value.trim().length < 1 || tempPromptValue.value.trim().length < 1)

// Prompt模板相关操作
const addPromptTemplate = () => {
function addPromptTemplate() {
for (const i of promptList.value) {
if (i.key === tempPromptKey.value) {
message.error(t('store.addRepeatTitleTips'))
Expand All @@ -106,7 +106,7 @@ const addPromptTemplate = () => {
changeShowModal('add')
}

const modifyPromptTemplate = () => {
function modifyPromptTemplate() {
let index = 0

// 通过临时索引把待修改项摘出来
Expand Down Expand Up @@ -135,19 +135,19 @@ const modifyPromptTemplate = () => {
changeShowModal('modify')
}

const deletePromptTemplate = (row: { key: string; value: string }) => {
function deletePromptTemplate(row: { key: string; value: string }) {
promptList.value = [
...promptList.value.filter((item: { key: string; value: string }) => item.key !== row.key),
] as never
message.success(t('common.deleteSuccess'))
}

const clearPromptTemplate = () => {
function clearPromptTemplate() {
promptList.value = []
message.success(t('common.clearSuccess'))
}

const importPromptTemplate = (from = 'online') => {
function importPromptTemplate(from = 'online') {
try {
const jsonData = JSON.parse(tempPromptValue.value)
let key = ''
Expand Down Expand Up @@ -196,7 +196,7 @@ const importPromptTemplate = (from = 'online') => {
}

// 模板导出
const exportPromptTemplate = () => {
function exportPromptTemplate() {
exportLoading.value = true
const jsonDataStr = JSON.stringify(promptList.value)
const blob = new Blob([jsonDataStr], { type: 'application/json' })
Expand All @@ -210,7 +210,7 @@ const exportPromptTemplate = () => {
}

// 模板在线导入
const downloadPromptTemplate = async () => {
async function downloadPromptTemplate() {
try {
importLoading.value = true
const response = await fetch(downloadURL.value)
Expand Down Expand Up @@ -239,7 +239,7 @@ const downloadPromptTemplate = async () => {
}

// 移动端自适应相关
const renderTemplate = () => {
function renderTemplate() {
const [keyLimit, valueLimit] = isMobile.value ? [10, 30] : [15, 50]

return promptList.value.map((item: { key: string; value: string }) => {
Expand All @@ -260,7 +260,7 @@ const pagination = computed(() => {
})

// table相关
const createColumns = (): DataTableColumns<DataProps> => {
function createColumns(): DataTableColumns<DataProps> {
return [
{
title: t('store.title'),
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Setting/Anonuncement.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang='ts'>
import { onMounted, ref } from 'vue'
import { NButton, NInput, NSpin, NSwitch, useMessage } from 'naive-ui'
import { AnnounceConfig, ConfigState } from './model'
import type { AnnounceConfig, ConfigState } from './model'
import { fetchChatConfig, fetchUpdateAnnounce } from '@/api'
import { t } from '@/locales'

Expand Down
3 changes: 1 addition & 2 deletions src/components/common/Setting/Audit.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script setup lang='ts'>
import { onMounted, ref } from 'vue'
import { NButton, NInput, NSelect, NSpin, NSwitch, useMessage } from 'naive-ui'
import type { AuditConfig, ConfigState } from './model'
import { TextAuditServiceProvider } from './model'
import type { AuditConfig, ConfigState, TextAuditServiceProvider } from './model'
import { fetchChatConfig, fetchTestAudit, fetchUpdateAudit } from '@/api'
import { t } from '@/locales'

Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Setting/Gift.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function parseCSV(content: string) {
return giftCards
}

function handleUploadChange(data: { file: UploadFileInfo, fileList: Array<UploadFileInfo>, event?: Event }) {
function handleUploadChange(data: { file: UploadFileInfo; fileList: Array<UploadFileInfo>; event?: Event }) {
fileListRef.value = data.fileList
csvData.value = []
if (data.event) {
Expand Down
3 changes: 1 addition & 2 deletions src/components/common/Setting/Keys.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const handleSaving = ref(false)
const keyConfig = ref(new KeyConfig('', 'ChatGPTAPI', [], [], ''))

const keys = ref([])
const createColumns = (): DataTableColumns => {
function createColumns(): DataTableColumns {
return [
{
title: 'Key',
Expand Down Expand Up @@ -226,7 +226,6 @@ onMounted(async () => {
</NButton>
</NSpace>
<NDataTable
ref="table"
remote
:loading="loading"
:row-key="(rowData) => rowData._id"
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Setting/Site.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ onMounted(() => {
/>
</div>
</div>
<!-- 增加新注册用户的全局数量设置 -->
<!-- 增加新注册用户的全局数量设置 -->
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.globalAmount') }}</span>
<div class="flex-1">
Expand Down
3 changes: 1 addition & 2 deletions src/components/common/Setting/User.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const userRef = ref(new UserInfo([UserRole.User]))

const users = ref([])

const createColumns = (): DataTableColumns => {
function createColumns(): DataTableColumns {
return [
{
title: 'Email',
Expand Down Expand Up @@ -279,7 +279,6 @@ onMounted(async () => {
</NButton>
</NSpace>
<NDataTable
ref="table"
remote
:loading="loading"
:row-key="(rowData) => rowData._id"
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Setting/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const apiModelOptions = ['ChatGPTAPI', 'ChatGPTUnofficialProxyAPI'].map((
}
})

export const userRoleOptions = Object.values(UserRole).filter(d => isNaN(Number(d))).map((role) => {
export const userRoleOptions = Object.values(UserRole).filter(d => Number.isNaN(Number(d))).map((role) => {
return {
label: role as string,
key: role as string,
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useIconRender.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { h } from 'vue'
import { SvgIcon } from '@/components/common'

export const useIconRender = () => {
export function useIconRender() {
interface IconConfig {
icon?: string
color?: string
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/scrollbarStyle.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { darkTheme, lightTheme } from 'naive-ui'

const setupScrollbarStyle = () => {
function setupScrollbarStyle() {
const style = document.createElement('style')
const styleContent = `
::-webkit-scrollbar {
Expand Down
2 changes: 1 addition & 1 deletion src/typings/chat.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ declare namespace Chat {

interface ChatState {
active: number | null
usingContext: boolean;
usingContext: boolean
history: History[]
chat: { uuid: number; data: Chat[] }[]
}
Expand Down
8 changes: 4 additions & 4 deletions src/typings/env.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/// <reference types="vite/client" />

interface ImportMetaEnv {
readonly VITE_GLOB_API_URL: string;
readonly VITE_APP_API_BASE_URL: string;
readonly VITE_GLOB_OPEN_LONG_REPLY: string;
readonly VITE_GLOB_APP_PWA: string;
readonly VITE_GLOB_API_URL: string
readonly VITE_APP_API_BASE_URL: string
readonly VITE_GLOB_OPEN_LONG_REPLY: string
readonly VITE_GLOB_APP_PWA: string
}
8 changes: 4 additions & 4 deletions src/typings/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
interface Window {
$loadingBar?: import('naive-ui').LoadingBarProviderInst;
$dialog?: import('naive-ui').DialogProviderInst;
$message?: import('naive-ui').MessageProviderInst;
$notification?: import('naive-ui').NotificationProviderInst;
$loadingBar?: import('naive-ui').LoadingBarProviderInst
$dialog?: import('naive-ui').DialogProviderInst
$message?: import('naive-ui').MessageProviderInst
$notification?: import('naive-ui').NotificationProviderInst
}
Loading