mirror of
https://github.com/coaidev/coai.git
synced 2025-05-25 16:00:15 +09:00
update package store
This commit is contained in:
parent
8d111a0a31
commit
f9aac47b67
@ -60,8 +60,9 @@ func TextChat(db *sql.DB, user *auth.User, conn *websocket.Conn, instance *conve
|
||||
SendSegmentMessage(conn, types.ChatGPTSegmentResponse{
|
||||
Message: defaultErrorMessage,
|
||||
Quota: buffer.GetQuota(),
|
||||
End: false,
|
||||
End: true,
|
||||
})
|
||||
return defaultErrorMessage
|
||||
}
|
||||
|
||||
// collect quota
|
||||
|
@ -29,6 +29,7 @@ import {login, tokenField} from "./conf.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Quota from "./routes/Quota.tsx";
|
||||
import {openDialog} from "./store/quota.ts";
|
||||
import Package from "./routes/Package.tsx";
|
||||
|
||||
function Settings() {
|
||||
const { t } = useTranslation();
|
||||
@ -113,6 +114,7 @@ function App() {
|
||||
<RouterProvider router={router} />
|
||||
<Toaster />
|
||||
<Quota />
|
||||
<Package />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
0
app/src/assets/package.less
Normal file
0
app/src/assets/package.less
Normal file
@ -30,6 +30,7 @@
|
||||
|
||||
.buy-button {
|
||||
width: 100%;
|
||||
transition: .25s;
|
||||
}
|
||||
}
|
||||
|
||||
|
34
app/src/conversation/addition.ts
Normal file
34
app/src/conversation/addition.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import axios from "axios";
|
||||
|
||||
type QuotaResponse = {
|
||||
status: boolean;
|
||||
error: string;
|
||||
}
|
||||
|
||||
type PackageResponse = {
|
||||
status: boolean;
|
||||
cert: boolean;
|
||||
teenager: boolean;
|
||||
}
|
||||
|
||||
export async function buyQuota(
|
||||
quota: number,
|
||||
): Promise<QuotaResponse> {
|
||||
try {
|
||||
const resp = await axios.post(`/buy`, { quota });
|
||||
return resp.data as QuotaResponse;
|
||||
} catch (e) {
|
||||
console.debug(e);
|
||||
return { status: false, error: "network error" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPackage(): Promise<PackageResponse> {
|
||||
try {
|
||||
const resp = await axios.get(`/package`);
|
||||
return resp.data as PackageResponse;
|
||||
} catch (e) {
|
||||
console.debug(e);
|
||||
return { status: false, cert: false, teenager: false };
|
||||
}
|
||||
}
|
@ -66,6 +66,23 @@ const resources = {
|
||||
"output": "Output",
|
||||
"tip": "Prices have been aligned (or lower) to OpenAI models",
|
||||
"learn-more": "Learn more",
|
||||
"dialog-title": "Buy Points",
|
||||
"dialog-desc": "Are you sure you want to buy {{amount}} points?",
|
||||
"dialog-cancel": "Cancel",
|
||||
"dialog-buy": "Buy",
|
||||
"success": "Purchase successful",
|
||||
"success-prompt": "You have successfully purchased {{amount}} points.",
|
||||
"failed": "Purchase failed",
|
||||
"failed-prompt": "Failed to purchase points. Please make sure you have enough balance, you will soon jump to deeptrain wallet to pay balance.",
|
||||
},
|
||||
pkg: {
|
||||
"title": "Packages",
|
||||
"go": "Go to",
|
||||
"verify": "Verify",
|
||||
"cert": "Certification Package",
|
||||
"cert-desc": "After real-name certification, you can get 50 points (worth 5 CNY)",
|
||||
"teen": "Teenager Package",
|
||||
"teen-desc": "After real-name certification, teenagers (18 years old and below) can get an additional 150 points (worth 15 CNY)",
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -124,6 +141,23 @@ const resources = {
|
||||
"output": "输出",
|
||||
"tip": "价格已对齐OpenAI模型或低于官方价格",
|
||||
"learn-more": "了解更多",
|
||||
"dialog-title": "购买点数",
|
||||
"dialog-desc": "您确定要购买 {{amount}} 点数吗?",
|
||||
"dialog-cancel": "取消",
|
||||
"dialog-buy": "购买",
|
||||
"success": "购买成功",
|
||||
"success-prompt": "您已成功购买 {{amount}} 点数。",
|
||||
"failed": "购买失败",
|
||||
"failed-prompt": "购买点数失败。请确保您有足够的余额,您即将跳转到 deeptrain 钱包支付余额。",
|
||||
},
|
||||
pkg: {
|
||||
"title": "礼包",
|
||||
"go": "前往",
|
||||
"verify": "实名认证",
|
||||
"cert": "实名认证礼包",
|
||||
"cert-desc": "实名认证后可获得 50 点数 (价值 5 元)",
|
||||
"teen": "未成年人福利",
|
||||
"teen-desc": "实名认证后未成年人(18 周岁及以下)可额外获得 150 点数 (价值 15 元)",
|
||||
}
|
||||
},
|
||||
},
|
||||
|
41
app/src/routes/Package.tsx
Normal file
41
app/src/routes/Package.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../components/ui/dialog.tsx";
|
||||
import {Button} from "../components/ui/button.tsx";
|
||||
import "../assets/package.less";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {dialogSelector, refreshPackage} from "../store/package.ts";
|
||||
import {useEffect} from "react";
|
||||
|
||||
function Package() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const open = useSelector(dialogSelector);
|
||||
useEffect(() => {
|
||||
refreshPackage(dispatch);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit profile</DialogTitle>
|
||||
<DialogDescription>
|
||||
Make changes to your profile here. Click save when you're done.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button>Save changes</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default Package;
|
@ -1,5 +1,5 @@
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {dialogSelector, refreshQuota, setDialog} from "../store/quota.ts";
|
||||
import {closeDialog, dialogSelector, refreshQuota, setDialog} from "../store/quota.ts";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useEffect, useState} from "react";
|
||||
import {
|
||||
@ -15,6 +15,16 @@ import {Input} from "../components/ui/input.tsx";
|
||||
import {testNumberInputEvent} from "../utils.ts";
|
||||
import {Button} from "../components/ui/button.tsx";
|
||||
import {Separator} from "../components/ui/separator.tsx";
|
||||
import {
|
||||
AlertDialog, AlertDialogAction, AlertDialogCancel,
|
||||
AlertDialogContent, AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTrigger
|
||||
} from "../components/ui/alert-dialog.tsx";
|
||||
import {AlertDialogTitle} from "@radix-ui/react-alert-dialog";
|
||||
import {buyQuota} from "../conversation/addition.ts";
|
||||
import {useToast} from "../components/ui/use-toast.ts";
|
||||
|
||||
type AmountComponentProps = {
|
||||
amount: number;
|
||||
@ -45,8 +55,9 @@ function AmountComponent({ amount, active, other, onClick }: AmountComponentProp
|
||||
|
||||
function Quota() {
|
||||
const { t } = useTranslation();
|
||||
const [ current, setCurrent ] = useState(0);
|
||||
const [ amount, setAmount ] = useState(0);
|
||||
const { toast } = useToast();
|
||||
const [ current, setCurrent ] = useState(1);
|
||||
const [ amount, setAmount ] = useState(10);
|
||||
const open = useSelector(dialogSelector);
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
@ -107,10 +118,43 @@ function Quota() {
|
||||
}
|
||||
</div>
|
||||
<div className={`buy-action`}>
|
||||
<Button variant={`default`} className={`buy-button`}>
|
||||
<Plus className={`h-4 w-4 mr-2`} />
|
||||
{ t('buy.buy', { amount }) }
|
||||
</Button>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant={`default`} className={`buy-button`} disabled={amount === 0}>
|
||||
<Plus className={`h-4 w-4 mr-2`} />
|
||||
{ t('buy.buy', { amount }) }
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{ t('buy.dialog-title') }</AlertDialogTitle>
|
||||
<AlertDialogDescription>{ t('buy.dialog-desc', { amount }) }</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{ t('buy.dialog-cancel') }</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={
|
||||
async () => {
|
||||
const res = await buyQuota(amount);
|
||||
if (res.status) {
|
||||
toast({
|
||||
title: t('buy.success'),
|
||||
description: t('buy.success-prompt', { amount }),
|
||||
})
|
||||
dispatch(closeDialog());
|
||||
} else {
|
||||
toast({
|
||||
title: t('buy.failed'),
|
||||
description: t('buy.failed-prompt', { amount }),
|
||||
})
|
||||
setTimeout(() => {
|
||||
window.open('https://deeptrain.lightxi.com/home/wallet');
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
}>{ t('buy.dialog-buy') }</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`line`} />
|
||||
|
@ -3,6 +3,7 @@ import menuReducer from "./menu";
|
||||
import authReducer from "./auth";
|
||||
import chatReducer from "./chat";
|
||||
import quotaReducer from "./quota";
|
||||
import packageReducer from "./package";
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
@ -10,6 +11,7 @@ const store = configureStore({
|
||||
auth: authReducer,
|
||||
chat: chatReducer,
|
||||
quota: quotaReducer,
|
||||
package: packageReducer,
|
||||
},
|
||||
});
|
||||
|
||||
|
47
app/src/store/package.ts
Normal file
47
app/src/store/package.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import {createSlice} from "@reduxjs/toolkit";
|
||||
import {getPackage} from "../conversation/addition.ts";
|
||||
|
||||
export const packageSlice = createSlice({
|
||||
name: "package",
|
||||
initialState: {
|
||||
dialog: false,
|
||||
cert: false,
|
||||
teenager: false,
|
||||
},
|
||||
reducers: {
|
||||
toggleDialog: (state) => {
|
||||
state.dialog = !state.dialog;
|
||||
},
|
||||
setDialog: (state, action) => {
|
||||
state.dialog = action.payload as boolean;
|
||||
},
|
||||
openDialog: (state) => {
|
||||
state.dialog = true;
|
||||
},
|
||||
closeDialog: (state) => {
|
||||
state.dialog = false;
|
||||
},
|
||||
refreshState: (state, action) => {
|
||||
state.cert = action.payload.cert;
|
||||
state.teenager = action.payload.teenager;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
export const {toggleDialog, setDialog, openDialog, closeDialog, refreshState} = packageSlice.actions;
|
||||
export default packageSlice.reducer;
|
||||
|
||||
export const dialogSelector = (state: any): boolean => state.package.dialog;
|
||||
export const certSelector = (state: any): boolean => state.package.cert;
|
||||
export const teenagerSelector = (state: any): boolean => state.package.teenager;
|
||||
|
||||
export const refreshPackage = (dispatch: any) => {
|
||||
setInterval(async () => {
|
||||
const current = new Date().getTime(); //@ts-ignore
|
||||
if (window.hasOwnProperty("package") && (current - window.package < 2500)) return; //@ts-ignore
|
||||
window.package = current;
|
||||
|
||||
const response = await getPackage();
|
||||
if (response.status) dispatch(refreshState(response));
|
||||
}, 10000);
|
||||
}
|
@ -75,10 +75,12 @@ func BuyAPI(c *gin.Context) {
|
||||
}
|
||||
|
||||
money := float32(form.Quota) * 0.1
|
||||
if Pay(user.Username, float32(money)*0.1) {
|
||||
if Pay(user.Username, money) {
|
||||
user.IncreaseQuota(db, float32(form.Quota))
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"status": true,
|
||||
"data": user.GetQuota(db),
|
||||
"error": "success",
|
||||
})
|
||||
} else {
|
||||
c.JSON(200, gin.H{
|
||||
|
Loading…
Reference in New Issue
Block a user