mirror of
https://github.com/coaidev/coai.git
synced 2025-05-20 05:20:15 +09:00
feat: refresh api key feature and image file preview feature
This commit is contained in:
parent
e5755a000d
commit
b6049e28ff
@ -32,5 +32,6 @@ func AnalysisRequest(model string, buffer *utils.Buffer, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IncrRequest(instance)
|
||||||
IncrModelRequest(instance, model, int64(buffer.CountToken()))
|
IncrModelRequest(instance, model, int64(buffer.CountToken()))
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "chatnio",
|
"productName": "chatnio",
|
||||||
"version": "3.8.0"
|
"version": "3.8.1"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
@ -5,8 +5,7 @@
|
|||||||
.file-instance {
|
.file-instance {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
margin: 2px 0 !important;
|
margin: 2px 0 !important;
|
||||||
margin-bottom: 4px !important;
|
margin-bottom: 4px !important;
|
||||||
padding: 6px 12px !important;
|
padding: 6px 12px !important;
|
||||||
@ -14,6 +13,22 @@
|
|||||||
background: hsla(var(--background-container)) !important;
|
background: hsla(var(--background-container)) !important;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
|
||||||
|
.file-content {
|
||||||
|
background: none !important;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 320px;
|
||||||
|
max-height: 240px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 668px) {
|
@media (max-width: 668px) {
|
||||||
& {
|
& {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { File } from "lucide-react";
|
import { File } from "lucide-react";
|
||||||
import { saveAsFile } from "@/utils/dom.ts";
|
import { saveAsFile } from "@/utils/dom.ts";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* file format:
|
* file format:
|
||||||
@ -12,6 +13,13 @@ import { saveAsFile } from "@/utils/dom.ts";
|
|||||||
export function parseFile(data: string) {
|
export function parseFile(data: string) {
|
||||||
const filename = data.split("\n")[0].replace("[[", "").replace("]]", "");
|
const filename = data.split("\n")[0].replace("[[", "").replace("]]", "");
|
||||||
const content = data.replace(`[[${filename}]]\n`, "");
|
const content = data.replace(`[[${filename}]]\n`, "");
|
||||||
|
const image = useMemo(() => {
|
||||||
|
// get image url from content (like: https://i.imgur.com/xxxxx.png)
|
||||||
|
const match = content
|
||||||
|
.toLowerCase()
|
||||||
|
.match(/(https?:\/\/.*\.(?:png|jpg|jpeg|gif))/);
|
||||||
|
return match ? match[0] : "";
|
||||||
|
}, [filename, content]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -23,8 +31,11 @@ export function parseFile(data: string) {
|
|||||||
saveAsFile(filename, content);
|
saveAsFile(filename, content);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<div className={`file-content`}>
|
||||||
<File className={`mr-1`} />
|
<File className={`mr-1`} />
|
||||||
<span className={`name`}>{filename}</span>
|
<span className={`name`}>{filename}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{image && <img src={image} className={`file-image`} alt={""} />}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import React from "react";
|
|||||||
import { syncSiteInfo } from "@/admin/api/info.ts";
|
import { syncSiteInfo } from "@/admin/api/info.ts";
|
||||||
import { loadPreferenceModels } from "@/utils/storage.ts";
|
import { loadPreferenceModels } from "@/utils/storage.ts";
|
||||||
|
|
||||||
export const version = "3.8.0-rc";
|
export const version = "3.8.1";
|
||||||
export const dev: boolean = getDev();
|
export const dev: boolean = getDev();
|
||||||
export const deploy: boolean = true;
|
export const deploy: boolean = true;
|
||||||
export let rest_api: string = getRestApi(deploy);
|
export let rest_api: string = getRestApi(deploy);
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
getApiKey,
|
getApiKey,
|
||||||
} from "@/store/api.ts";
|
} from "@/store/api.ts";
|
||||||
import { Input } from "@/components/ui/input.tsx";
|
import { Input } from "@/components/ui/input.tsx";
|
||||||
import { Copy, ExternalLink } from "lucide-react";
|
import { Copy, ExternalLink, RotateCw } from "lucide-react";
|
||||||
import { useToast } from "@/components/ui/use-toast.ts";
|
import { useToast } from "@/components/ui/use-toast.ts";
|
||||||
import { copyClipboard } from "@/utils/dom.ts";
|
import { copyClipboard } from "@/utils/dom.ts";
|
||||||
import { useEffectAsync } from "@/utils/hook.ts";
|
import { useEffectAsync } from "@/utils/hook.ts";
|
||||||
@ -53,6 +53,13 @@ function ApikeyDialog() {
|
|||||||
<DialogDescription asChild>
|
<DialogDescription asChild>
|
||||||
<div className={`api-dialog`}>
|
<div className={`api-dialog`}>
|
||||||
<div className={`api-wrapper`}>
|
<div className={`api-wrapper`}>
|
||||||
|
<Button
|
||||||
|
variant={`outline`}
|
||||||
|
size={`icon`}
|
||||||
|
onClick={() => getApiKey(dispatch)}
|
||||||
|
>
|
||||||
|
<RotateCw className={`h-4 w-4`} />
|
||||||
|
</Button>
|
||||||
<Input value={key} />
|
<Input value={key} />
|
||||||
<Button variant={`default`} size={`icon`} onClick={copyKey}>
|
<Button variant={`default`} size={`icon`} onClick={copyKey}>
|
||||||
<Copy className={`h-4 w-4`} />
|
<Copy className={`h-4 w-4`} />
|
||||||
|
@ -8,7 +8,9 @@ export async function copyClipboard(text: string) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (navigator.clipboard) return await navigator.clipboard.writeText(text);
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
return await navigator.clipboard.writeText(text);
|
||||||
|
}
|
||||||
|
|
||||||
const el = document.createElement("textarea");
|
const el = document.createElement("textarea");
|
||||||
el.value = text;
|
el.value = text;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"chat/admin"
|
|
||||||
"chat/utils"
|
"chat/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -62,7 +61,6 @@ func ThrottleMiddleware() gin.HandlerFunc {
|
|||||||
ip := c.ClientIP()
|
ip := c.ClientIP()
|
||||||
path := c.Request.URL.Path
|
path := c.Request.URL.Path
|
||||||
cache := utils.GetCacheFromContext(c)
|
cache := utils.GetCacheFromContext(c)
|
||||||
admin.IncrRequest(cache)
|
|
||||||
|
|
||||||
limiter := GetPrefixMap[Limiter](path, limits)
|
limiter := GetPrefixMap[Limiter](path, limits)
|
||||||
if limiter != nil && limiter.RateLimit(cache, ip, path) {
|
if limiter != nil && limiter.RateLimit(cache, ip, path) {
|
||||||
|
@ -58,5 +58,11 @@ func RegisterStaticRoute(engine *gin.Engine) {
|
|||||||
c.File("./app/dist/index.html")
|
c.File("./app/dist/index.html")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// redirect /v1 to /api/v1
|
||||||
|
engine.Any("/v1/*path", func(c *gin.Context) {
|
||||||
|
path := c.Param("path")
|
||||||
|
c.Redirect(301, fmt.Sprintf("/api/v1/%s", path))
|
||||||
|
})
|
||||||
|
|
||||||
fmt.Println(`[service] start serving static files from ~/app/dist`)
|
fmt.Println(`[service] start serving static files from ~/app/dist`)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user