update subscription usage form

This commit is contained in:
Zhang Minghan 2023-10-25 23:48:41 +08:00
parent 65eb601d84
commit 74be9cb54d
10 changed files with 112 additions and 33 deletions

View File

@ -6,23 +6,51 @@
display: flex;
flex-direction: column;
margin: 18px 4px !important;
margin-bottom: 32px !important;
margin-bottom: 48px !important;
align-items: center;
}
.date {
.sub-row {
display: flex;
flex-direction: row;
align-items: center;
color: hsl(45, 100%, 50%);
flex-direction: column;
padding: 8px 16px;
margin-top: 16px;
margin-bottom: 8px;
border-radius: var(--radius);
border: 1px solid hsl(var(--border));
align-items: center;
svg {
flex-shrink: 0;
transform: translateY(1px);
.sub-column {
display: flex;
flex-direction: row;
align-items: center;
color: hsl(45, 100%, 50%);
margin-bottom: 4px;
width: 100%;
svg {
flex-shrink: 0;
transform: translateY(1px);
}
.sub-value {
display: flex;
flex-direction: row;
align-items: center;
gap: 2px;
p {
font-weight: bolder;
}
}
&:first-child {
margin-bottom: 8px;
}
&:last-child {
margin-bottom: 0;
}
}
}

View File

@ -1,7 +1,7 @@
import axios from "axios";
import { Model } from "./conversation/types.ts";
export const version = "3.5.12";
export const version = "3.5.13";
export const dev: boolean = window.location.hostname === "localhost";
export const deploy: boolean = true;
export let rest_api: string = "http://localhost:8094";

View File

@ -15,6 +15,10 @@ type SubscriptionResponse = {
status: boolean;
is_subscribed: boolean;
expired: number;
usage: {
gpt4: number;
dalle: number;
}
};
type BuySubscriptionResponse = {
@ -58,16 +62,12 @@ export async function getSubscription(): Promise<SubscriptionResponse> {
try {
const resp = await axios.get(`/subscription`);
if (resp.data.status === false) {
return { status: false, is_subscribed: false, expired: 0 };
return { status: false, is_subscribed: false, expired: 0, usage: { gpt4: 0, dalle: 0 } };
}
return {
status: resp.data.status,
is_subscribed: resp.data.is_subscribed,
expired: resp.data.expired,
};
return resp.data as SubscriptionResponse;
} catch (e) {
console.debug(e);
return { status: false, is_subscribed: false, expired: 0 };
return { status: false, is_subscribed: false, expired: 0 , usage: { gpt4: 0, dalle: 0 } };
}
}

View File

@ -4,7 +4,7 @@ import {
isSubscribedSelector,
refreshSubscription,
refreshSubscriptionTask,
setDialog,
setDialog, usageSelector,
} from "../store/subscription.ts";
import {
Dialog,
@ -158,6 +158,7 @@ function Subscription() {
const open = useSelector(dialogSelector);
const subscription = useSelector(isSubscribedSelector);
const expired = useSelector(expiredSelector);
const usage = useSelector(usageSelector);
const dispatch = useDispatch();
useEffect(() => {
refreshSubscriptionTask(dispatch);
@ -174,9 +175,27 @@ function Subscription() {
<DialogDescription asChild>
<div className={`sub-wrapper`}>
{subscription && (
<div className={`date`}>
<Calendar className={`h-4 w-4 mr-1`} />
{t("sub.expired", { expired })}
<div className={`sub-row`}>
<div className={`sub-column`}>
<Calendar className={`h-4 w-4 mr-1`} />
{t("sub.expired", { expired })}
</div>
<div className={`sub-column`}>
<Compass className={`h-4 w-4 mr-1`} />
GPT-4
<div className={`grow`} />
<div className={`sub-value`}>
<p>{ usage.gpt4 }</p> / <p> 50 </p>
</div>
</div>
<div className={`sub-column`}>
<ImagePlus className={`h-4 w-4 mr-1`} />
DALL-E
<div className={`grow`} />
<div className={`sub-value`}>
<p>{ usage.dalle }</p> / <p> 2000 </p>
</div>
</div>
</div>
)}
<div className={`plan-wrapper`}>

View File

@ -8,6 +8,10 @@ export const subscriptionSlice = createSlice({
dialog: false,
is_subscribed: false,
expired: 0,
usage: {
gpt4: 0,
dalle: 0,
}
},
reducers: {
toggleDialog: (state) => {
@ -25,6 +29,7 @@ export const subscriptionSlice = createSlice({
updateSubscription: (state, action) => {
state.is_subscribed = action.payload.is_subscribed;
state.expired = action.payload.expired;
state.usage = action.payload.usage;
},
},
});
@ -44,6 +49,8 @@ export const isSubscribedSelector = (state: any): boolean =>
state.subscription.is_subscribed;
export const expiredSelector = (state: any): number =>
state.subscription.expired;
export const usageSelector = (state: any): any =>
state.subscription.usage;
export const refreshSubscription = async (dispatch: AppDispatch) => {
const current = new Date().getTime(); //@ts-ignore

View File

@ -62,10 +62,12 @@ func SubscriptionAPI(c *gin.Context) {
}
db := utils.GetDBFromContext(c)
cache := utils.GetCacheFromContext(c)
c.JSON(200, gin.H{
"status": true,
"is_subscribed": user.IsSubscribe(db),
"expired": user.GetSubscriptionExpiredDay(db),
"usage": user.GetSubscriptionUsage(db, cache),
})
}

View File

@ -1,11 +1,10 @@
package auth
import (
"chat/globals"
"chat/utils"
"database/sql"
"fmt"
"github.com/go-redis/redis/v8"
"time"
)
func CountSubscriptionPrize(month int) float32 {
@ -33,13 +32,11 @@ func BuySubscription(db *sql.DB, user *User, month int) bool {
}
func IncreaseSubscriptionUsage(cache *redis.Client, user *User) bool {
today := time.Now().Format("2006-01-02")
return utils.IncrWithLimit(cache, fmt.Sprintf(":subscription-usage:%s:%d", today, user.ID), 1, 50, 60*60*24) // 1 day
return utils.IncrWithLimit(cache, globals.GetGPT4LimitFormat(user.ID), 1, 50, 60*60*24) // 1 day
}
func DecreaseSubscriptionUsage(cache *redis.Client, user *User) bool {
today := time.Now().Format("2006-01-02")
return utils.DecrInt(cache, fmt.Sprintf(":subscription-usage:%s:%d", today, user.ID), 1)
return utils.DecrInt(cache, globals.GetGPT4LimitFormat(user.ID), 1)
}
func CanEnableSubscription(db *sql.DB, cache *redis.Client, user *User) bool {

View File

@ -1,12 +1,14 @@
package auth
import (
"chat/globals"
"chat/utils"
"database/sql"
"errors"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
"github.com/spf13/viper"
"math"
"net/http"
@ -162,6 +164,21 @@ func (u *User) GetSubscriptionExpiredDay(db *sql.DB) int {
return int(math.Round(stamp.Hours() / 24))
}
type Usage struct {
GPT4 int64 `json:"gpt4"`
Dalle int64 `json:"dalle"`
}
func (u *User) GetSubscriptionUsage(db *sql.DB, cache *redis.Client) Usage {
gpt4, _ := utils.GetInt(cache, globals.GetGPT4LimitFormat(u.GetID(db)))
dalle, _ := utils.GetInt(cache, globals.GetImageLimitFormat(u.GetID(db)))
return Usage{
GPT4: gpt4,
Dalle: dalle,
}
}
func (u *User) AddSubscription(db *sql.DB, month int) bool {
current := u.GetSubscription(db)
if current.Unix() < time.Now().Unix() {

14
globals/quota.go Normal file
View File

@ -0,0 +1,14 @@
package globals
import (
"fmt"
"time"
)
func GetImageLimitFormat(id int64) string {
return fmt.Sprintf(":imagelimit:%s:%d", time.Now().Format("2006-01-02"), id)
}
func GetGPT4LimitFormat(id int64) string {
return fmt.Sprintf(":subscription-usage:%s:%d", time.Now().Format("2006-01-02"), id)
}

View File

@ -3,18 +3,13 @@ package manager
import (
"chat/adapter/chatgpt"
"chat/auth"
"chat/globals"
"chat/utils"
"database/sql"
"fmt"
"github.com/gin-gonic/gin"
"strings"
"time"
)
func GetImageLimitFormat(db *sql.DB, user *auth.User) string {
return fmt.Sprintf(":imagelimit:%s:%d", time.Now().Format("2006-01-02"), user.GetID(db))
}
func GenerateImage(c *gin.Context, user *auth.User, prompt string) (string, error) {
// free plan: 5 images per day
// pro plan: 50 images per day
@ -22,7 +17,7 @@ func GenerateImage(c *gin.Context, user *auth.User, prompt string) (string, erro
db := utils.GetDBFromContext(c)
cache := utils.GetCacheFromContext(c)
key := GetImageLimitFormat(db, user)
key := globals.GetImageLimitFormat(user.GetID(db))
usage := auth.GetDalleUsageLimit(db, user)
prompt = strings.TrimSpace(prompt)