feat: support close relay api (#111 #112)

This commit is contained in:
Zhang Minghan 2024-03-13 14:25:02 +08:00
parent 4f4bc1561b
commit 40cb56761d
12 changed files with 73 additions and 17 deletions

View File

@ -33,6 +33,7 @@ export type SearchState = {
export type SiteState = {
close_register: boolean;
close_relay: boolean;
relay_plan: boolean;
quota: number;
buy_link: string;
@ -123,8 +124,9 @@ export const initialSystemState: SystemProps = {
pwa_manifest: "",
},
site: {
relay_plan: false,
close_register: false,
close_relay: false,
relay_plan: false,
quota: 0,
buy_link: "",
announcement: "",

View File

@ -706,6 +706,8 @@
"searchPlaceholder": "DuckDuckGo 接入点 (格式仅需填写 https://example.com)",
"closeRegistration": "暂停注册",
"closeRegistrationTip": "暂停注册,关闭后新用户将无法注册",
"closeRelay": "关闭中转 API",
"closeRelayTip": "关闭中转 API关闭后中转 API 将无法使用",
"relayPlan": "订阅配额支持中转 API",
"relayPlanTip": "订阅配额支持中转 API开启后中转 API 计费会优先考虑使用用户订阅配额\n提示订阅为次数配额对 Token 计费的模型可能会影响成本)",
"quota": "用户初始点数",

View File

@ -564,7 +564,9 @@
"searchPlaceholder": "DuckDuckGo Access Point (Format only https://example.com)",
"image_store": "Picture storage",
"image_storeTip": "Images generated by the OpenAI channel DALL-E will be stored on the server to prevent invalidation of the images",
"image_storeNoBackend": "No backend domain configured, cannot enable image storage"
"image_storeNoBackend": "No backend domain configured, cannot enable image storage",
"closeRelay": "Turn off Staging API",
"closeRelayTip": "Turn off the staging API, the staging API will not be available after turning off"
},
"user": "Users",
"invitation-code": "Invitation Code",

View File

@ -564,7 +564,9 @@
"searchPlaceholder": "DuckDuckGoアクセスポイントフォーマットのみhttps://example.com ",
"image_store": "画像ストレージ",
"image_storeTip": "OpenAIチャンネルDALL - Eによって生成された画像は、画像の無効化を防ぐためにサーバーに保存されます",
"image_storeNoBackend": "バックエンドドメインが設定されていません。画像ストレージを有効にできません"
"image_storeNoBackend": "バックエンドドメインが設定されていません。画像ストレージを有効にできません",
"closeRelay": "ステージングAPIをオフにする",
"closeRelayTip": "ステージングAPIをオフにすると、オフにするとステージングAPIは使用できなくなります"
},
"user": "ユーザー管理",
"invitation-code": "招待コード",

View File

@ -564,7 +564,9 @@
"searchPlaceholder": "Точка доступа DuckDuckGo (только в формате https://example.com)",
"image_store": "Хранение изображений",
"image_storeTip": "Изображения, сгенерированные каналом OpenAI DALL-E, будут храниться на сервере, чтобы предотвратить недействительность изображений",
"image_storeNoBackend": "Нет настроенного внутреннего домена, невозможно включить хранение изображений"
"image_storeNoBackend": "Нет настроенного внутреннего домена, невозможно включить хранение изображений",
"closeRelay": "Отключить Staging API",
"closeRelayTip": "Отключите промежуточный API, промежуточный API будет недоступен после отключения"
},
"user": "Управление пользователями",
"invitation-code": "Код приглашения",

View File

@ -44,7 +44,7 @@ import {
} from "@/components/ui/dialog.tsx";
import { DialogTitle } from "@radix-ui/react-dialog";
import Require from "@/components/Require.tsx";
import { Loader2, PencilLine, Settings2 } from "lucide-react";
import { PencilLine, RotateCw, Save, Settings2 } from "lucide-react";
import { FlexibleTextarea } from "@/components/ui/textarea.tsx";
import Tips from "@/components/Tips.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
@ -497,6 +497,21 @@ function Site({ data, dispatch, onChange }: CompProps<SiteState>) {
}}
/>
</ParagraphItem>
<ParagraphItem>
<Label>
{t("admin.system.closeRelay")}
<Tips
className={`inline-block`}
content={t("admin.system.closeRelayTip")}
/>
</Label>
<Switch
checked={data.close_relay}
onCheckedChange={(value) => {
dispatch({ type: "update:site.close_relay", value });
}}
/>
</ParagraphItem>
<ParagraphItem>
<Label>
{t("admin.system.relayPlan")}
@ -843,7 +858,7 @@ function System() {
if (doToast !== false) toastState(toast, t, res, true);
};
useEffectAsync(async () => {
const doRefresh = async () => {
setLoading(true);
const res = await getConfig();
setLoading(false);
@ -851,18 +866,36 @@ function System() {
if (res.status) {
setData({ type: "set", value: res.data });
}
}, []);
};
useEffectAsync(doRefresh, []);
return (
<div className={`system`}>
<Card className={`admin-card system-card`}>
<CardHeader className={`select-none`}>
<CardTitle>
{t("admin.settings")}
{loading && <Loader2 className={`h-4 w-4 ml-2 inline-block`} />}
</CardTitle>
<CardTitle>{t("admin.settings")}</CardTitle>
</CardHeader>
<CardContent className={`flex flex-col gap-1`}>
<div className={`system-actions flex flex-row`}>
<div className={`grow`} />
<Button
size={`icon`}
variant={`outline`}
loading={true}
className={`mr-2`}
onClick={async () => await doRefresh()}
>
<RotateCw className={cn(loading && `animate-spin`, `h-4 w-4`)} />
</Button>
<Button
size={`icon`}
loading={true}
onClick={async () => await doSaving()}
>
<Save className={`h-4 w-4`} />
</Button>
</div>
<General
form={data}
data={data.general}

View File

@ -196,7 +196,7 @@ func DeepLogin(c *gin.Context, token string) (string, error) {
db := utils.GetDBFromContext(c)
if !IsUserExist(db, user.Username) {
if channel.SystemInstance.IsCloseRegister() {
if globals.CloseRegistration {
return "", errors.New("this site is not open for registration")
}

View File

@ -2,6 +2,7 @@ package auth
import (
"chat/channel"
"chat/globals"
"chat/utils"
"github.com/gin-gonic/gin"
"net/http"
@ -143,7 +144,7 @@ func RegisterAPI(c *gin.Context) {
return
}
if channel.SystemInstance.IsCloseRegister() {
if globals.CloseRegistration {
c.JSON(http.StatusOK, gin.H{
"status": false,
"error": "this site is not open for registration",

View File

@ -35,6 +35,7 @@ type generalState struct {
type siteState struct {
CloseRegister bool `json:"close_register" mapstructure:"closeregister"`
CloseRelay bool `json:"close_relay" mapstructure:"closerelay"`
RelayPlan bool `json:"relay_plan" mapstructure:"relayplan"`
Quota float64 `json:"quota" mapstructure:"quota"`
BuyLink string `json:"buy_link" mapstructure:"buylink"`
@ -94,6 +95,9 @@ func NewSystemConfig() *SystemConfig {
func (c *SystemConfig) Load() {
globals.NotifyUrl = c.GetBackend()
globals.CloseRegistration = c.Site.CloseRegister
globals.CloseRelay = c.Site.CloseRelay
globals.ArticlePermissionGroup = c.Common.Article
globals.GenerationPermissionGroup = c.Common.Generation
globals.CacheAcceptedModels = c.Common.Cache
@ -282,10 +286,6 @@ func (c *SystemConfig) AcceptImageStore() bool {
return c.Common.ImageStore
}
func (c *SystemConfig) IsCloseRegister() bool {
return c.Site.CloseRegister
}
func (c *SystemConfig) SupportRelayPlan() bool {
return c.Site.RelayPlan
}

View File

@ -18,6 +18,8 @@ var CacheAcceptedModels []string
var CacheAcceptedExpire int64
var CacheAcceptedSize int64
var AcceptImageStore bool
var CloseRegistration bool
var CloseRelay bool
func OriginIsAllowed(uri string) bool {
if len(AllowedOrigins) == 0 {

View File

@ -26,6 +26,11 @@ func supportRelayPlan() bool {
}
func ChatRelayAPI(c *gin.Context) {
if globals.CloseRelay {
abortWithErrorResponse(c, fmt.Errorf("relay api is denied of access"), "access_denied_error")
return
}
username := utils.GetUserFromContext(c)
if username == "" {
abortWithErrorResponse(c, fmt.Errorf("access denied for invalid api key"), "authentication_error")

View File

@ -15,6 +15,11 @@ import (
)
func ImagesRelayAPI(c *gin.Context) {
if globals.CloseRelay {
abortWithErrorResponse(c, fmt.Errorf("relay api is denied of access"), "access_denied_error")
return
}
username := utils.GetUserFromContext(c)
if username == "" {
abortWithErrorResponse(c, fmt.Errorf("access denied for invalid api key"), "authentication_error")