}Rjl6MDBcwI+m(O$3AhA6UlWTt7{=}L)=TLJ
zbaK^wbz>8Ax$CaGzOiA0`3W#j=3XIjKzGfWHES1gpU`b-p^2gAlMNnrb*n!{p|RfVC~z`AsdF227*#rQUiw=B
zGlrZlg+0c>d9|)yH*z<17zBAZ$<3wVXb#OCcB&FaK>8fd<(iP2jq_Zx_!8iTx%Taz
znhk2-!}@jW&-sZ=L3e{>K9YKC>$dW;xcx{Kd?kkWI5UAy)TRh>=e6o5$I)s>Z%RbISc!F-kT9Tuh}D;`dV
znmEOiS{c`({*}@~##T6ZixFOq2NuVEF>TsfG?x6w?IoXA>X((xKPJ!=A6c7GNwI@s
zg1>~}@%0yRKt7Xy%v^v+AAR&Yk3II-3R`+Fy{`a7uf6u#tNQSG`ivRomm}M;Y&ICh
z#5(wg7rEwN&Y_)4WYN^X1nJmH=$L3JJNmRrIXQaq;5lO_K{raxr7y2M_=2*c
z>KMVt80+&0g0Ec>p+HQJ`Tj)DKlj|qrAwFI&gH8~pOZdN0D4egUC){|>l6C+R&~L|
zO`hrD)*N2k3|u?g{^q}6k`+MEY;3uM(QywuxNqmEEi)AzBXnQh+N3NO05r^***te6
z<`sgtbc_-95v7$Cajl=v{eJnC5fpP5(O6=8@$k~GUV8cFn{WO?hNPPJqYo5-NS~bR
zgJXSoY%D<&X1(tj^!o^Q9Lr&Mqk}Ila!?)TOi6(vm5wfRFsH`DUOLbo23MgH3P~C>
zUD$?37^+k1=k;^COK>j1h6q~VBg}`X`KvE`wV@_h^-Uq9yI&0{=72EnD@O8U&V*Gm^th~^`Yn^3qYhe;c5NR)Ft+NVnco=C>OdNoAn`@%iypr{ahLhc8=45
zwlQwfH$+Q1{<||KFePINkjoK?Ljr
zCon`sNtl{4WLk
zY@#_jddK+CLk~Un?z`{q%VslqVSH5P7M@cd>Pvm9Z-1DNjXw4c&!&+hgFm02J$vfZ
zDdx}TYib7nd|qwz3$=cvZ6WFBsy%yl@7b}VVf)678-LG#J`em~b4F)$d;mUX00000
LNkvXXu0mjfIs7~s
literal 8085
zcmeHM_cI(0u-AKu8cvOb-09`??(`_vkSG!T^dwG#J`k%+d
z^oKSD1=Sy@1=7gmp~XMX|I7b(;QwF;?g
zFp=Y7pcjiS>)$xPI-Py_P@7uHrD$lYE7Mw-Nk3)acbfvO-qgjX5Q*^7<7?#yb8)w+
z;O`4$mxJa9D>2iPnhs@|Vi+av`&U!U3aQuY;@tl*UOYpQcj
zbZ1XqOs_Y3dz0V%>GiOYai|Ag?cJbIwUR~Dd_?mjR2q4wvY;U_*0J{I-F9x?CpTca
z|9uB7#gA#`V|%{Pp-Y@TPcLY^dEtL=g`+C<$jaS+_=;&G53F)>1^8}a4*-6b_sc(?&dEa!Rl
zt3~!IGs`=bOp4O}N;hsW-kV0+SOo}|-RLUG@cAs86idVX0_tBL?QzX9lvcU4o&=z(
zt}6epWoiNfWlgmtmmPaDREg*1^L}GmFl^|99?dX|@?Eg`n0RR$a*i1JGj7Jq3sw`2
zoV2eQ`pPv{vm307lr*ujE$j-gRh*`#EY=DLRJuw?R91L-uC0E7>pB4U?m61uzgHeJ
z34hdFa~npPBPCEU=kWbk*7$nbLBo_RPX;2kZ7}{Yc*lQod+AEd9>6XMvb%69QY_Uf
z%B8Hy*pv6S{7dHOR2SA(FVxj!Z)wp=Z?Erq>df9e8w(#eeOST5blCPd!f=}NWc2s1
z#Lx4e#=V3uK^viq-O2ofT%TFnqor)``4wO`>}XTjBW!$O_zF{?^=CJNI-odxXYQ$9
z?!?vA!SxyP8`kLf_PX>I9xlkE1w$pcBcD;4;$R}iRh$!)k69{eIkTO$MM~ta4CZ2z
z-w)|@P3&r{wGO}E+v*H>`*Ue;6%qYi>BsFr$suqdKdaL_iF7>1^D79+b;kR18q9Ho
z1~@95`L{~p2iq}KHizN)cjYN+C1H610!ooCy0bTVVYN%HEiRwoGY_T@)n=KEVF7)j
zEnXf{GopSr1%@HtH<1Hz;P~&87G*LTtOt)tA7`>RT6mwlWqAT35YL0|N@X|be
zeHjH1tK^sZgd{FKy~G0
z>AlJ*C-_+nN-Mj5z;A;N*W$0ehtMi|?Dk)eTloDS6G>D#C4nYKY{(Hj)~sc>Z4Y$T
z3kdm`-nN=efF?f~nQ;H9K>Ef@-%KNtUP)&9CabWUX72Fo0HLy!aLDz+;6N;=~i?4uO)-bZt0K8EUNa=0@wx
z+P+$k3@n8ugXoE+_wSjUir)U7y>YBy&m=+
z<)&lfKBPsT?!a%H=|3{HY_!QQ!ld+ej}EFozbyE$=TXuvr|8;y{#8{f7PoY;DU#SQ
z>R~mzI5A8KXjd`V3Fo+#NJ<8!sq-60f65;j>mb%>p5vv6B}vjn4s?onN#~j9
z38)6=D8uNlkLP%pvq!@u=s*=rPrV)!kl`&KpF8j>u682pH2tgE+n^t?mQ{dJ(MXqm
zO_i*>&&a^;)=1;(x%Z__ax+^mkELwe8KaBGts1nXpmFS;Xy&(s)e7wV=xxplEp4cL-rPRPm-$V+#vy6xgiOdQDyqIBX8#c027
zBs*H9!4h$K1D%m
zm2b_!Q3CC}o1c200^dDi_*=aEG#_sCTmKX73xa;~&9E+fe(jkp@!8ojqjBU9nyw?8
zc1*h{n_^-g>e^#&*FNC$<6G_AB#gp9oKqpL>o-c*hUOSh6*P*ai(|S4bLVL1BFzh$
z5A8)q{{F>jtu;R?@)Jn$1(VTTupDvBkEiS!8I
z|7$F@s6g9QFE5*tP)H7Afo#>F;)+k8kQ>M=#q7i@59Q_mux30)Qv42G280{eP>MNJ
zH6%5W&Gm_EQ|S!43I&p$B4~ATHFqeLRYFoNIYtp7k`k4Z9gH9@e&Lsm56h;C_^K{M
z`ffacyx5dLzQ$%tQf>CzN2CL22{#e9y6}Rm6HAdmWd%88PK^mq|0-?X6&b^0+z5mY
ze;tKtTJR$BCva)_7geQ~PM5pfsbNIyN?}4Q{<2nxKpr0}aFnLOeyW!O3)Ha=j(N
z*v}IW`VA&FAzTst;t)iyIx_RzjE6!`8$h!i)$MBr4&z9}0Y(aVlOpJye7h=>zFZft
zrsra-j*wk3uQaGRUu2WP724NC4sTP~EDtBHD}YVU5CsCye}2t_>hB4aJSPW(M$pSo
zM0seWXShqzkfT*rfMxtme9^X{M})7w-K->u!4y6t`NKSV!72{mm}B2G6jf{geIvhe
z!YU{w?KGSyo^DB~_#l_2hj{ok9BqW*+b$oy*hdpTy1W2o$;B%2J3;VO(RUzd=lE`Z
zK;aphNaX>{I!1l&Wlumr*G!
zA1vGVJh0_sRZI^7lj}XQ-)l*8bV)ukivV^19X-f$*$1R*h=(a=JN875?jy2pW5(15TaiRaH
zB`^s`!2$rcY|v`t3%TBprdejuT=aTV)Lz~m$1Z$4`0fUww0Mm+MqaDoN{e5mH%HLB
z2h#Z%0jde_jNotGP{I`#lW*{tWq_CdlxgyK8a-VvN|JRfR*0FWRO6mf7z8#z-ePld
zvl|`Uej<-DkDaS2WNLJk&aU|@o}!p;8&_+88Sj@)z_OgI8gT&Xsx(L^M3|MYX;M
z$2~qdu;yy#@iAXQtplFsDYmg2*q!NQ3nWFhY!~jqT>bWdbQET`OsX-IeSNfTZfpt@C^=NSiAvl
zzik{$`f=+b&ef^p^n#8)0B`E!Ch{hpYM`ln
z&}+0VBbkbMaG3TTmo#_D-S?^;0!;ej1#nj{vB4U!P@;pvD`T@>7rc_V&ep;NRIjU<
z1oKmnZa0j6R55;IphbsDW!{I{1j0WwXNDIEf3VA#Uno&*mAP97t>^cs2p^vrg&KGGSA24S&NuJTdDNdO
zAGi|h!C}0%fN@}uHugx=NNlOZb$JIk;CH3oh6}qdr_j0)TCeBAc^Xfb;oBzu7t_ssh-1*PbYdCvy
z9ElLx1I)@!HArQ^>vvB6Dzrv0`CG@@+dAGr8(^VqHTQf=i!*;xSHvpsfF8etqb;Mx+mDI%y1O^Rj4|uK
zQIzGKgN!noN%Uh5_?~@ho8My}m6Y+mpPont`sN*|2Jou!Nix&%1de=cFb9n+2iDup
zK7w-*LmRa=kVFXhB@+Q&1rA!&Kr74h=gfL>b1&~$YFsnNy1s4=Pqdb`V_yeo76&+*
zT2#YQ+2VaV3h0)2PJgCqw6{^W{+PAQBg)Nm8;%@bmSy4D4rK^k>A;FA4SZ3ipt23iPV@%$R}xUGh2RkQUYc{Z?Y1`{!Ql>$M!!Ew43GMpBc9le5DZw3TN504n3%z($w6@x08K
z7VnGj_W2y9#a+a1bC@>6-jT3T`f_}9WcjMt4ap1DqMz3z_9g9U+Li0W_qdP>SV8m-
z>PNH>HlI?B(?=jRNt_gc>wI*S{E!437isl;T^ch0q3|;3vqfMsM)zn~HNX`2|0YTr
z9A?MW#x8lgn7fIn@%U6MCR|}vQ5lUi6rDSFcn|=&=!*5g!+wP9e5O#88@{u0l-rpv
zg{4e?!<|B}Zn60X*IY^UFdt4qDlJYjqwH6GPr3{E7fV-SjfftPRFgsfr|;vyEUeH&
zMY13h3vQI>sO0tpze!Yed`&2}+!
zkhgU|v|=MR@u3#dh6%k*A6>kHUk+Rzxc*=!ujtDc>%5nh?9^!q8vNGLzpqV`p76`R
zg-Ve=g~;)9(ptFgff&?8BoC;WKK6qWW~idYi!BIAO1(GgXWX|oFWRD14R%UD-2f)o
zQ{cNX#4^%Bv&mMl5ojFkd99suv#OMvzZA<+CVZ>)Iw2|U4QW1b^SIoi%mp*|7U~Rl
zcT6e$WNrPKU7@g4oA1+8js=oC8?Wet}kF=qevlbY{?VnC-#WknUEa&`3;Ary0U
zZw2OupnSF*6T5L&Kx91+S8MO1Yj7o0){Bf?%?=5@PwIC=T*q>rAwLg=}2yKhnk9jrdFs
zn2EezAv;1e72_@(v<#Nf>0q>6t4)>gXK8f5?IF=Ss|jY}qwsLvt0?u7G?>a7GS!5z
zwj1W9g5=MPwNe2Pr!6~sXm?&~M%EjX(J6UHF@_uD$;5+HVy(-3$33e`J?UY|t
z7h*=7EYGh?dFoC1zW1(O=cs{F^9m8(N1Z|^o>N6!E+n0Ny~_LpXIu~4L~_b3PloM?
zDkjnpz0K)q_TTqQO@Fb3-2dT#
z9K~a$Eo{{FWGWYuG`P**P_ZBFa9Z&=pGM)jqsj8YGLD?+cmvEl>~le6o&U``Hs$Tl
zl-DQ9ztM$^%hfdjnXZzi6mLM(BQX*?NIgsQ40wI%AWeoTwK&=9jC&p#FZ*%ue(>HFFf(UL8+>pLfP%Vs2aFlXk>Qq~0>%28N~`xr7ze
z;vWZjcC-HDER)=(hV{SOGo=Wte4rN|FOo=;iLcg~dDHT;o?7--6xEjnSn-&}mz;-2
zX>z4RS0k&IMOmZHSg)P(s=`MjIg>|xHhzLsQ{j5i18ej5{z{=FAG_XLe^xV+EeTgG
z`=AT~9)h%Fev8`^o8?)PnpBEQ)!N;89l5m%s9?$qk2;4?D<773|JET7X7X-Hs*`{2
z?vXkcqHfBK+P`0HoY9v>ZMeWt4{cH;VBYz(mtjFPBE(J0)SacSaXyDv?qq~_1
z+PErdYAT=4trr50kr-b+|As4ip`gtl;QgZ5VDE!|0Rg4dEe}7VS*d`A$-hEw(ua-J
zP(HK^N@`4a=Nehs|4N3k*}vp&C4*Xx7q|A}w{~cY|Mk9}`>{t&PfGt#ZSF-~$i_qc
z*`kGp5uU*I)i8}6c!vSgcHJ{Y-R|U4DZvsM
zgZOT?JF8I79_f&Yh5szQ6TFY+J1JiPZRmeY?uDz*;Ey<3s-P0(xl6(sinYT2@@=xS
z9N(WkF1&mcxbA$#H)l6{T^92Wv|=pMr+w2ONf<>>kqtN=n$1e)GZ2$81DSzuik?+s
zW^@=M-UTF#4{Elo#bJ*h5Z<|=JS~*-^PR7U6>UB@M<_oGWCDQ0;R}xMDi4ZDjgsT<
z59ai>lg1otPeD-LGgFD7!X9lI@hAHOTTW6oq{x%aiKo_f`R9fO?e9ttt{lDfnP2!W
z$=o24@gdhJwo2z(IWM4EuW%f4!3t;nwO&J7CoGe4Do@N
ze0?Ll3dwo@{iYp#v(Ab@iu1V*Nd0WW%y%7Ycgm%TI%dE*^Zlzb;Uhg0A6&3A;
z3$@v&ySaopv0}FWB8WeaG8=5Zylt^Q`DAnG#Ewv+qf_R=MT~yt>(o&FW*9<9bP;$PZo3SFV)wKG!23>VdH*S2$tpd9+h8
vm{h2v9Dij;CQqNNYK7N)pA@v57rGMY^ijcdQjk#p`&BhFvNEhSco6eHk%;zp
From 5f3798053faf940afa15131c17752e96f916b766 Mon Sep 17 00:00:00 2001
From: Calcium-Ion <61247483+Calcium-Ion@users.noreply.github.com>
Date: Wed, 18 Sep 2024 01:41:31 +0800
Subject: [PATCH 16/25] Create FUNDING.yml
---
.github/FUNDING.yml | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 .github/FUNDING.yml
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..8774778
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: ['https://afdian.com/a/new-api'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
From 052bc2075b877f341d92dcd31ef698cdbb84baf2 Mon Sep 17 00:00:00 2001
From: CalciumIon <1808837298@qq.com>
Date: Wed, 18 Sep 2024 05:19:10 +0800
Subject: [PATCH 17/25] =?UTF-8?q?feat:=20=E4=BB=A4=E7=89=8C=E5=88=86?=
=?UTF-8?q?=E7=BB=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
common/user_groups.go | 23 ++
controller/group.go | 15 +
controller/token.go | 2 +
middleware/auth.go | 1 +
middleware/distributor.go | 9 +
model/option.go | 3 +
model/token.go | 4 +-
router/api-router.go | 1 +
web/src/components/OperationSetting.js | 2 +
web/src/components/TokensTable.js | 11 +-
web/src/helpers/render.js | 4 +-
.../Operation/SettingsMagnification.js | 30 +-
web/src/pages/Token/EditToken.js | 263 ++++++++++--------
13 files changed, 248 insertions(+), 120 deletions(-)
create mode 100644 common/user_groups.go
diff --git a/common/user_groups.go b/common/user_groups.go
new file mode 100644
index 0000000..67c3e79
--- /dev/null
+++ b/common/user_groups.go
@@ -0,0 +1,23 @@
+package common
+
+import (
+ "encoding/json"
+)
+
+var UserUsableGroups = map[string]string{
+ "default": "默认分组",
+ "vip": "vip分组",
+}
+
+func UserUsableGroups2JSONString() string {
+ jsonBytes, err := json.Marshal(UserUsableGroups)
+ if err != nil {
+ SysError("error marshalling user groups: " + err.Error())
+ }
+ return string(jsonBytes)
+}
+
+func UpdateUserUsableGroupsByJSONString(jsonStr string) error {
+ UserUsableGroups = make(map[string]string)
+ return json.Unmarshal([]byte(jsonStr), &UserUsableGroups)
+}
diff --git a/controller/group.go b/controller/group.go
index 2b2f600..2ee008b 100644
--- a/controller/group.go
+++ b/controller/group.go
@@ -17,3 +17,18 @@ func GetGroups(c *gin.Context) {
"data": groupNames,
})
}
+
+func GetUserGroups(c *gin.Context) {
+ usableGroups := make(map[string]string)
+ for groupName, _ := range common.GroupRatio {
+ // UserUsableGroups contains the groups that the user can use
+ if _, ok := common.UserUsableGroups[groupName]; ok {
+ usableGroups[groupName] = common.UserUsableGroups[groupName]
+ }
+ }
+ c.JSON(http.StatusOK, gin.H{
+ "success": true,
+ "message": "",
+ "data": usableGroups,
+ })
+}
diff --git a/controller/token.go b/controller/token.go
index 50a368f..0fc4b6c 100644
--- a/controller/token.go
+++ b/controller/token.go
@@ -135,6 +135,7 @@ func AddToken(c *gin.Context) {
ModelLimitsEnabled: token.ModelLimitsEnabled,
ModelLimits: token.ModelLimits,
AllowIps: token.AllowIps,
+ Group: token.Group,
}
err = cleanToken.Insert()
if err != nil {
@@ -223,6 +224,7 @@ func UpdateToken(c *gin.Context) {
cleanToken.ModelLimitsEnabled = token.ModelLimitsEnabled
cleanToken.ModelLimits = token.ModelLimits
cleanToken.AllowIps = token.AllowIps
+ cleanToken.Group = token.Group
}
err = cleanToken.Update()
if err != nil {
diff --git a/middleware/auth.go b/middleware/auth.go
index 481960e..8426b04 100644
--- a/middleware/auth.go
+++ b/middleware/auth.go
@@ -176,6 +176,7 @@ func TokenAuth() func(c *gin.Context) {
c.Set("token_model_limit_enabled", false)
}
c.Set("allow_ips", token.GetIpLimitsMap())
+ c.Set("token_group", token.Group)
if len(parts) > 1 {
if model.IsAdmin(token.UserId) {
c.Set("specific_channel_id", parts[1])
diff --git a/middleware/distributor.go b/middleware/distributor.go
index 9b55cc2..0393d24 100644
--- a/middleware/distributor.go
+++ b/middleware/distributor.go
@@ -39,6 +39,15 @@ func Distribute() func(c *gin.Context) {
return
}
userGroup, _ := model.CacheGetUserGroup(userId)
+ tokenGroup := c.GetString("token_group")
+ if tokenGroup != "" {
+ // check group in common.GroupRatio
+ if _, ok := common.GroupRatio[tokenGroup]; !ok {
+ abortWithOpenAiMessage(c, http.StatusForbidden, fmt.Sprintf("分组 %s 已被禁用", tokenGroup))
+ return
+ }
+ userGroup = tokenGroup
+ }
c.Set("group", userGroup)
if ok {
id, err := strconv.Atoi(channelId.(string))
diff --git a/model/option.go b/model/option.go
index 4348919..04f952d 100644
--- a/model/option.go
+++ b/model/option.go
@@ -86,6 +86,7 @@ func InitOptionMap() {
common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString()
common.OptionMap["ModelPrice"] = common.ModelPrice2JSONString()
common.OptionMap["GroupRatio"] = common.GroupRatio2JSONString()
+ common.OptionMap["UserUsableGroups"] = common.UserUsableGroups2JSONString()
common.OptionMap["CompletionRatio"] = common.CompletionRatio2JSONString()
common.OptionMap["TopUpLink"] = common.TopUpLink
common.OptionMap["ChatLink"] = common.ChatLink
@@ -303,6 +304,8 @@ func updateOptionMap(key string, value string) (err error) {
err = common.UpdateModelRatioByJSONString(value)
case "GroupRatio":
err = common.UpdateGroupRatioByJSONString(value)
+ case "UserUsableGroups":
+ err = common.UpdateUserUsableGroupsByJSONString(value)
case "CompletionRatio":
err = common.UpdateCompletionRatioByJSONString(value)
case "ModelPrice":
diff --git a/model/token.go b/model/token.go
index 18aa297..dc769c8 100644
--- a/model/token.go
+++ b/model/token.go
@@ -25,6 +25,7 @@ type Token struct {
ModelLimits string `json:"model_limits" gorm:"type:varchar(1024);default:''"`
AllowIps *string `json:"allow_ips" gorm:"default:''"`
UsedQuota int `json:"used_quota" gorm:"default:0"` // used quota
+ Group string `json:"group" gorm:"default:''"`
DeletedAt gorm.DeletedAt `gorm:"index"`
}
@@ -153,7 +154,8 @@ func (token *Token) Insert() error {
// Update Make sure your token's fields is completed, because this will update non-zero values
func (token *Token) Update() error {
var err error
- err = DB.Model(token).Select("name", "status", "expired_time", "remain_quota", "unlimited_quota", "model_limits_enabled", "model_limits", "allow_ips").Updates(token).Error
+ err = DB.Model(token).Select("name", "status", "expired_time", "remain_quota", "unlimited_quota",
+ "model_limits_enabled", "model_limits", "allow_ips", "group").Updates(token).Error
return err
}
diff --git a/router/api-router.go b/router/api-router.go
index 6807939..c38a314 100644
--- a/router/api-router.go
+++ b/router/api-router.go
@@ -39,6 +39,7 @@ func SetApiRouter(router *gin.Engine) {
//userRoute.POST("/tokenlog", middleware.CriticalRateLimit(), controller.TokenLog)
userRoute.GET("/logout", controller.Logout)
userRoute.GET("/epay/notify", controller.EpayNotify)
+ userRoute.GET("/groups", controller.GetUserGroups)
selfRoute := userRoute.Group("/")
selfRoute.Use(middleware.UserAuth())
diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js
index a61b757..1d875c6 100644
--- a/web/src/components/OperationSetting.js
+++ b/web/src/components/OperationSetting.js
@@ -23,6 +23,7 @@ const OperationSetting = () => {
CompletionRatio: '',
ModelPrice: '',
GroupRatio: '',
+ UserUsableGroups: '',
TopUpLink: '',
ChatLink: '',
ChatLink2: '', // 添加的新状态变量
@@ -62,6 +63,7 @@ const OperationSetting = () => {
if (
item.key === 'ModelRatio' ||
item.key === 'GroupRatio' ||
+ item.key === 'UserUsableGroups' ||
item.key === 'CompletionRatio' ||
item.key === 'ModelPrice'
) {
diff --git a/web/src/components/TokensTable.js b/web/src/components/TokensTable.js
index 64a189f..74b249a 100644
--- a/web/src/components/TokensTable.js
+++ b/web/src/components/TokensTable.js
@@ -8,14 +8,14 @@ import {
} from '../helpers';
import { ITEMS_PER_PAGE } from '../constants';
-import { renderQuota } from '../helpers/render';
+import {renderGroup, renderQuota} from '../helpers/render';
import {
Button,
Dropdown,
Form,
Modal,
Popconfirm,
- Popover,
+ Popover, Space,
SplitButtonGroup,
Table,
Tag,
@@ -119,7 +119,12 @@ const TokensTable = () => {
dataIndex: 'status',
key: 'status',
render: (text, record, index) => {
- return {renderStatus(text, record.model_limits_enabled)}
;
+ return
+
+ {renderStatus(text, record.model_limits_enabled)}
+ {renderGroup(record.group)}
+
+
;
},
},
{
diff --git a/web/src/helpers/render.js b/web/src/helpers/render.js
index ce53148..54e74f2 100644
--- a/web/src/helpers/render.js
+++ b/web/src/helpers/render.js
@@ -15,8 +15,8 @@ export function renderText(text, limit) {
export function renderGroup(group) {
if (group === '') {
return (
-
- unknown
+
+ default
);
}
diff --git a/web/src/pages/Setting/Operation/SettingsMagnification.js b/web/src/pages/Setting/Operation/SettingsMagnification.js
index 212080c..ef9a1c2 100644
--- a/web/src/pages/Setting/Operation/SettingsMagnification.js
+++ b/web/src/pages/Setting/Operation/SettingsMagnification.js
@@ -16,7 +16,8 @@ export default function SettingsMagnification(props) {
ModelPrice: '',
ModelRatio: '',
CompletionRatio: '',
- GroupRatio: ''
+ GroupRatio: '',
+ UserUsableGroups: ''
});
const refForm = useRef();
const [inputsRow, setInputsRow] = useState(inputs);
@@ -213,6 +214,33 @@ export default function SettingsMagnification(props) {
/>
+
+
+ {
+ return verifyJSON(value);
+ },
+ message: '不是合法的 JSON 字符串'
+ }
+ ]}
+ onChange={(value) =>
+ setInputs({
+ ...inputs,
+ UserUsableGroups: value
+ })
+ }
+ />
+
+
diff --git a/web/src/pages/Token/EditToken.js b/web/src/pages/Token/EditToken.js
index 64aa719..e18f24b 100644
--- a/web/src/pages/Token/EditToken.js
+++ b/web/src/pages/Token/EditToken.js
@@ -35,6 +35,7 @@ const EditToken = (props) => {
model_limits_enabled: false,
model_limits: [],
allow_ips: '',
+ group: '',
};
const [inputs, setInputs] = useState(originInputs);
const {
@@ -44,10 +45,12 @@ const EditToken = (props) => {
unlimited_quota,
model_limits_enabled,
model_limits,
- allow_ips
+ allow_ips,
+ group
} = inputs;
// const [visible, setVisible] = useState(false);
const [models, setModels] = useState({});
+ const [groups, setGroups] = useState([]);
const navigate = useNavigate();
const handleInputChange = (name, value) => {
setInputs((inputs) => ({ ...inputs, [name]: value }));
@@ -88,6 +91,22 @@ const EditToken = (props) => {
}
};
+ const loadGroups = async () => {
+ let res = await API.get(`/api/user/groups`);
+ const { success, message, data } = res.data;
+ if (success) {
+ // return data is a map, key is group name, value is group description
+ // label is group description, value is group name
+ let localGroupOptions = Object.keys(data).map((group) => ({
+ label: data[group],
+ value: group,
+ }));
+ setGroups(localGroupOptions);
+ } else {
+ showError(message);
+ }
+ };
+
const loadToken = async () => {
setLoading(true);
let res = await API.get(`/api/token/${props.editingToken.id}`);
@@ -120,6 +139,7 @@ const EditToken = (props) => {
});
}
loadModels();
+ loadGroups();
}, [isEdit]);
// 新增 state 变量 tokenCount 来记录用户想要创建的令牌数量,默认为 1
@@ -253,150 +273,150 @@ const EditToken = (props) => {
>
handleInputChange('name', value)}
- value={name}
- autoComplete='new-password'
- required={!isEdit}
+ style={{marginTop: 20}}
+ label='名称'
+ name='name'
+ placeholder={'请输入名称'}
+ onChange={(value) => handleInputChange('name', value)}
+ value={name}
+ autoComplete='new-password'
+ required={!isEdit}
/>
-
+
handleInputChange('expired_time', value)}
- value={expired_time}
- autoComplete='new-password'
- type='dateTime'
+ label='过期时间'
+ name='expired_time'
+ placeholder={'请选择过期时间'}
+ onChange={(value) => handleInputChange('expired_time', value)}
+ value={expired_time}
+ autoComplete='new-password'
+ type='dateTime'
/>
-
+
-
+
-
+
{`额度${renderQuotaWithPrompt(remain_quota)}`}
handleInputChange('remain_quota', value)}
- value={remain_quota}
- autoComplete='new-password'
- type='number'
- // position={'top'}
- data={[
- { value: 500000, label: '1$' },
- { value: 5000000, label: '10$' },
- { value: 25000000, label: '50$' },
- { value: 50000000, label: '100$' },
- { value: 250000000, label: '500$' },
- { value: 500000000, label: '1000$' },
- ]}
- disabled={unlimited_quota}
+ style={{marginTop: 8}}
+ name='remain_quota'
+ placeholder={'请输入额度'}
+ onChange={(value) => handleInputChange('remain_quota', value)}
+ value={remain_quota}
+ autoComplete='new-password'
+ type='number'
+ // position={'top'}
+ data={[
+ {value: 500000, label: '1$'},
+ {value: 5000000, label: '10$'},
+ {value: 25000000, label: '50$'},
+ {value: 50000000, label: '100$'},
+ {value: 250000000, label: '500$'},
+ {value: 500000000, label: '1000$'},
+ ]}
+ disabled={unlimited_quota}
/>
{!isEdit && (
- <>
-
- 新建数量
-
- handleTokenCountChange(value)}
- onSelect={(value) => handleTokenCountChange(value)}
- value={tokenCount.toString()}
- autoComplete='off'
- type='number'
- data={[
- { value: 10, label: '10个' },
- { value: 20, label: '20个' },
- { value: 30, label: '30个' },
- { value: 100, label: '100个' },
- ]}
- disabled={unlimited_quota}
- />
- >
+ <>
+
+ 新建数量
+
+ handleTokenCountChange(value)}
+ onSelect={(value) => handleTokenCountChange(value)}
+ value={tokenCount.toString()}
+ autoComplete='off'
+ type='number'
+ data={[
+ {value: 10, label: '10个'},
+ {value: 20, label: '20个'},
+ {value: 30, label: '30个'},
+ {value: 100, label: '100个'},
+ ]}
+ disabled={unlimited_quota}
+ />
+ >
)}
-
-