Compare commits

..

2 Commits

Author SHA1 Message Date
1iaan
5ce2a5f1a3 compose 2026-04-10 12:00:03 +08:00
1iaan
94bdba930e frontend 兼容 2026-04-06 11:38:45 +08:00
206 changed files with 29047 additions and 34763 deletions

7
.env
View File

@@ -1,7 +0,0 @@
MOONSHOT_API_KEY=sk-8NMdsGbDAMpWdd6hrKHepr1tNVXTy2QppKAqJkoJcHd6TYLs
# frontend 对外端口
FRONTEND_PORT=1025
# ai-chat-service embedding 配置
AI_CHAT_EMBEDDING_API_KEY=d51b903546814cc9981d3649a4a899a3.NQOtz3ocRtQwimh9

View File

@@ -8,36 +8,12 @@ protoc \
``` ```
```shell ```shell
# chatgpt-web-backend # ai-chat-web
go mod tidy
go run cmd/main.go \
--frontend-path www \
--openapi-key $MOONSHOT_API_KEY \
--openapi-base-url https://api.moonshot.cn/v1 \
--openai-model kimi-k2.5 \
--openai-temperature 100 \
--openai-presence-penalty 0 \
--openai-frequency-penalty 0
docker build -t chatgpt-web-backend:1.0.0 .
docker run -d --name chatgpt-web-backend \
-p 7080:7080 \
chatgpt-web-backend:1.0.0 \
/app/server \
--frontend-path www \
--openapi-key "$MOONSHOT_API_KEY" \
--openapi-base-url https://api.moonshot.cn/v1 \
--openai-model kimi-k2.5 \
--openai-temperature 100 \
--openai-presence-penalty 0 \
--openai-frequency-penalty 0
# ai-chat-backend # ai-chat-backend
docker build -t ai-chat-backend:1.0.0 . docker build -t ai-chat-backend:1.0.0 .
# ai-chat-service # ai-chat-service
GOCACHE=/tmp/ai-chat-service-gocache go build -o ai-chat-service-bin ./chat-server
docker build -t ai-chat-service:1.0.0 . docker build -t ai-chat-service:1.0.0 .
# chatgpt-web-frontend # chatgpt-web-frontend

View File

@@ -7,7 +7,7 @@ log:
logPath: "runtime/logs/app.log" logPath: "runtime/logs/app.log"
chat: chat:
# 使用的训练模型 # 使用的训练模型
model: "kimi-k2.5" model: "kimi-k2-turbo-preview"
# 单次请求的上下文总长度,包括:请求消息+响应消息 # 单次请求的上下文总长度,包括:请求消息+响应消息
max_tokens: 4096 max_tokens: 4096
# 表示语言模型输出的随机性和创造性 # 表示语言模型输出的随机性和创造性

View File

@@ -6,7 +6,7 @@ log:
level: "info" level: "info"
logPath: "runtime/logs/app.log" logPath: "runtime/logs/app.log"
chat: chat:
model: "kimi-k2.5" model: "kimi-k2-turbo-preview"
max_tokens: 4096 max_tokens: 4096
temperature: 1 temperature: 1
top_p: 1 top_p: 1

View File

@@ -14,7 +14,7 @@ type Config struct {
BasicAuthUser string `mapstructure:"basic_auth_user"` BasicAuthUser string `mapstructure:"basic_auth_user"`
BasicAuthPassword string `mapstructure:"basic_auth_password"` BasicAuthPassword string `mapstructure:"basic_auth_password"`
FrontendPath string `mapstructure:"frontend_path"` FrontendPath string `mapstructure:"frontend_path"`
Log struct { Log struct {
Level string Level string
LogPath string `mapstructure:"logPath"` LogPath string `mapstructure:"logPath"`
} `mapstructure:"log"` } `mapstructure:"log"`
@@ -74,7 +74,7 @@ func normalizeConfig(conf *Config) {
conf.FrontendPath = "www" conf.FrontendPath = "www"
} }
if conf.Chat.Model == "" { if conf.Chat.Model == "" {
conf.Chat.Model = "kimi-k2.5" conf.Chat.Model = "kimi-k2-turbo-preview"
} }
if conf.Chat.MaxTokens == 0 { if conf.Chat.MaxTokens == 0 {
conf.Chat.MaxTokens = 4096 conf.Chat.MaxTokens = 4096

View File

@@ -0,0 +1,5 @@
.git
.gitignore
ai-chat-service-bin
runtime/logs
runtime/*.log

View File

@@ -1,11 +1,34 @@
FROM golang:1.25 AS builder
ENV GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct \
GOSUMDB=sum.golang.google.cn \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
WORKDIR /src/ai-chat-service
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o /out/ai-chat-service ./chat-server
FROM alpine:3.18 FROM alpine:3.18
ADD ./grpc_health_probe-linux-amd64 /usr/bin/grpc_health_probe ADD ./grpc_health_probe-linux-amd64 /usr/bin/grpc_health_probe
RUN chmod +x /usr/bin/grpc_health_probe RUN chmod +x /usr/bin/grpc_health_probe
LABEL maintainer="nick" LABEL maintainer="nick"
WORKDIR /app/ WORKDIR /app/
COPY ./ai-chat-service-bin ./ai-chat-service
COPY --from=builder /out/ai-chat-service ./ai-chat-service
COPY ./docker.config.yaml /app/config.yaml COPY ./docker.config.yaml /app/config.yaml
# 指定入口程序
ENTRYPOINT ["./ai-chat-service"] ENTRYPOINT ["./ai-chat-service"]
# 指定容器的启动命令或者入口程序的参数
CMD ["--config=config.yaml"] CMD ["--config=config.yaml"]

View File

@@ -6,9 +6,9 @@ log:
level: "info" level: "info"
logPath: "runtime/logs/app.log" logPath: "runtime/logs/app.log"
chat: chat:
api_key: "sk-8NMdsGbDAMpWdd6hrKHepr1tNVXTy2QppKAqJkoJcHd6TYLs" api_key: "xxxxxxxxxxx"
base_url: "https://api.moonshot.cn/v1" base_url: "https://api.moonshot.cn/v1"
model: "kimi-k2.5" model: "kimi-k2-turbo-preview"
max_tokens: 4096 max_tokens: 4096
temperature: 1 temperature: 1
top_p: 0.95 top_p: 0.95

View File

@@ -2,6 +2,7 @@ package config
import ( import (
"log" "log"
"os"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@@ -113,6 +114,7 @@ func InitConfig(filePath string, typ ...string) {
log.Fatal(err) log.Fatal(err)
} }
normalizeConfig(conf) normalizeConfig(conf)
applySecretEnvOverrides(conf)
} }
@@ -167,3 +169,15 @@ func normalizeConfig(conf *Config) {
conf.Embedding.Timeout = 10 conf.Embedding.Timeout = 10
} }
} }
func applySecretEnvOverrides(conf *Config) {
if v := os.Getenv("MOONSHOT_API_KEY"); v != "" {
conf.Chat.ApiKey = v
}
if v := os.Getenv("AI_CHAT_EMBEDDING_API_KEY"); v != "" {
conf.Embedding.ApiKey = v
}
if v := os.Getenv("REDIS_PASSWORD"); v != "" {
conf.Redis.Pwd = v
}
}

View File

@@ -7,8 +7,6 @@ services:
MYSQL_ROOT_PASSWORD: root MYSQL_ROOT_PASSWORD: root
command: command:
- --default-authentication-plugin=mysql_native_password - --default-authentication-plugin=mysql_native_password
# ports:
# - "3306:3306"
volumes: volumes:
- /data/mysql:/var/lib/mysql - /data/mysql:/var/lib/mysql
- /home/lian/share/aichat/init/create_db.sql:/docker-entrypoint-initdb.d/create_db.sql:ro - /home/lian/share/aichat/init/create_db.sql:/docker-entrypoint-initdb.d/create_db.sql:ro
@@ -33,20 +31,22 @@ services:
- /home/lian/share/aichat/init/pgvector-init.sql:/docker-entrypoint-initdb.d/pgvector-init.sql:ro - /home/lian/share/aichat/init/pgvector-init.sql:/docker-entrypoint-initdb.d/pgvector-init.sql:ro
tokenizer: tokenizer:
build:
context: ../tokenizer
image: tokenizer:1.0.0 image: tokenizer:1.0.0
container_name: tokenizer container_name: tokenizer
# 容器内端口3002
ports: ports:
- "3002:3002" - "3002:3002"
restart: unless-stopped restart: unless-stopped
sensitive-filter: sensitive-filter:
build:
context: ../keywords-filter
image: keywords-filter:1.0.0 image: keywords-filter:1.0.0
container_name: sensitive-filter container_name: sensitive-filter
# 容器内端口50053
volumes: volumes:
- /home/lian/share/aichat/keywords-filter/dev.config.yaml:/app/config.yaml:ro - /home/lian/share/aichat/ai-chat-stack/configs/sensitive.yaml:/app/config.yaml:ro
- /home/lian/share/aichat/keywords-filter/dict.txt:/app/dict.txt:ro - /home/lian/share/aichat/ai-chat-stack/configs/sensitive-dict.txt:/app/dict.txt:ro
command: command:
- --config=/app/config.yaml - --config=/app/config.yaml
- --dict=/app/dict.txt - --dict=/app/dict.txt
@@ -55,12 +55,13 @@ services:
restart: unless-stopped restart: unless-stopped
keywords-filter: keywords-filter:
build:
context: ../keywords-filter
image: keywords-filter:1.0.0 image: keywords-filter:1.0.0
container_name: keywords-filter container_name: keywords-filter
# 容器内端口50054
volumes: volumes:
- /home/lian/share/aichat/keywords-filter/dev.kw.config.yaml:/app/config.yaml:ro - /home/lian/share/aichat/ai-chat-stack/configs/keywords.yaml:/app/config.yaml:ro
- /home/lian/share/aichat/keywords-filter/keyword-dict.txt:/app/dict.txt:ro - /home/lian/share/aichat/ai-chat-stack/configs/keywords-dict.txt:/app/dict.txt:ro
command: command:
- --config=/app/config.yaml - --config=/app/config.yaml
- --dict=/app/dict.txt - --dict=/app/dict.txt
@@ -70,11 +71,13 @@ services:
ai-chat-service: ai-chat-service:
build: build:
context: ./ai-chat-service context: ../ai-chat-service
image: ai-chat-service:1.0.0 image: ai-chat-service:1.0.0
container_name: ai-chat-service container_name: ai-chat-service
env_file:
- .env
volumes: volumes:
- /home/lian/share/aichat/ai-chat-service/docker.config.yaml:/app/config.yaml:ro - /home/lian/share/aichat/ai-chat-stack/configs/ai-chat-service.yaml:/app/config.yaml:ro
extra_hosts: extra_hosts:
- "host.docker.internal:host-gateway" - "host.docker.internal:host-gateway"
ports: ports:
@@ -92,24 +95,26 @@ services:
retries: 5 retries: 5
restart: unless-stopped restart: unless-stopped
chatgpt-web-backend: ai-chat-backend:
build: build:
context: ./ai-chat-backend context: ../ai-chat-backend
image: ai-chat-backend:1.0.0 image: ai-chat-backend:1.0.0
container_name: chatgpt-web-backend container_name: ai-chat-backend
# 容器内端口7080 ports:
- "7080:7080"
volumes: volumes:
- /home/lian/share/aichat/ai-chat-backend/docker.config.yaml:/app/config.yaml:ro - /home/lian/share/aichat/ai-chat-stack/configs/ai-chat-backend.yaml:/app/config.yaml:ro
depends_on: depends_on:
- ai-chat-service - ai-chat-service
restart: unless-stopped restart: unless-stopped
chatgpt-web-frontend: ai-chat-web:
image: chatgpt-web-frontend:1.0.0 build:
container_name: chatgpt-web-frontend context: ../ai-chat-web
image: ai-chat-web:1.0.0
container_name: ai-chat-web
depends_on: depends_on:
- chatgpt-web-backend - ai-chat-backend
# 容器内端口80
ports: ports:
- "${FRONTEND_PORT}:80" - "${FRONTEND_PORT:-1025}:80"
restart: unless-stopped restart: unless-stopped

View File

@@ -0,0 +1,22 @@
http:
ip: 0.0.0.0
port: 7080
frontend_path: "www"
log:
level: "info"
logPath: "runtime/logs/app.log"
chat:
model: "kimi-k2-turbo-preview"
max_tokens: 4096
temperature: 1
top_p: 1
presence_penalty: 0
frequency_penalty: 0
bot_desc: "你是一个AI助手我需要你模拟一名资深的软件工程师来回答我的问题"
min_response_tokens: 600
context_ttl: 1800
context_len: 4
dependOn:
ai-chat-service:
address: "ai-chat-service:50055"
accessToken: "me256487ang1chubdpdialoud22sev1ozhoguumyqca"

View File

@@ -0,0 +1,54 @@
server:
ip: 0.0.0.0
port: 50055
accessToken: "me256487ang1chubdpdialoud22sev1ozhoguumyqca"
log:
level: "info"
logPath: "runtime/logs/app.log"
chat:
api_key: "__SET_FROM_ENV__"
base_url: "https://api.moonshot.cn/v1"
model: "kimi-k2-turbo-preview" # kimi-k2.5
max_tokens: 4096
temperature: 1
top_p: 0.95
presence_penalty: 0
frequency_penalty: 0
bot_desc: "你是一个AI助手我需要你模拟一名资深的软件工程师来回答我的问题"
min_response_tokens: 600
context_ttl: 1800
context_len: 4
redis:
host: "host.docker.internal"
port: 8888
pwd: "123456"
mysql:
dsn: "root:root@tcp(mysql:3306)/ai_chat?collation=utf8mb4_unicode_ci&charset=utf8mb4"
maxLifeTime: 3600
maxOpenConn: 10
maxIdleConn: 10
dependOn:
sensitive:
address: "sensitive-filter:50053"
accessToken: "ang1chubdev1ozhome256487d22sapguuv1ozhom"
keywords:
address: "keywords-filter:50054"
accessToken: "ang1chubdev1ozhome256487d22sapguuv1ozhom"
tokenizer:
address: "http://tokenizer:3002"
vector:
provider: "pgvector"
threshold: 0.99
pgvector:
dsn: "postgres://postgres:postgres@pgvector:5432/ai_chat?sslmode=disable"
table: "chat_record_vectors"
dimensions: 1024
maxLifeTime: 3600
maxOpenConn: 10
maxIdleConn: 10
embedding:
provider: "openai-compatible"
base_url: "https://open.bigmodel.cn/api/paas/v4"
api_key: "__SET_FROM_ENV__"
model: "embedding-2"
timeout: 10

View File

@@ -0,0 +1,192 @@
golang
defer
recover
sync
Protobuf
gin
grpc-gateway
OpenTelemetry
OTel
otel
k8s
Kubernetes
kubernetes
Docker
docker
Istio
istio
Prometheus
prometheus
cadvisor
cAdvisor
Elastic
Kibana
Grafana
apiserver
CI/CD
ci/cd
ArgoCD
argo
Argo
kaniko
Mesh
Volume
volume
promQL
PromQL
kafka
ingress
StorageClass
VolumeClaim
gitlab
openflow
dpdk
vpp
ovs
spdk
virtio
vhost
qemu
vSwitch
bridge
hugepage
nvme
dpvs
iperf3
rfc2544
ioengine
PCI
vxlan
gre
kni
Kernel
内核
KernelThread
内核线程
Virtual
memory
虚拟内存
内存屏障
内存管理
Scheduler
调度器
File
文件系统
Device
driver
设备驱动程序
Syscall
系统调用
Process
scheduling
进程调度
Page
页表
Swap
交换空间
Mount
Inode
挂载
索引节点
Block
块设备
Character
字符设备
IRQ
Kconfig
内核配置
Perf
Ftrace
内核跟踪工具
Valgrind
内存调试工具
System
系统定时器
DMA
伙伴系统
信号与槽
Signals
Slots
事件处理程序
Event
QML
多线程编程
Multithreading
Programming
QThread
QtQuick
模型
视图架构
Model/View
QObject
QWidget
QRegularExpression
QDesktopWidget
QNetworkAccessManager
QTcpServer
QTcpSocket
QUdpSocket
QMutex
SQLite/MySQL
MySQL编程
SQLite编程
OpenCV
OpenGL
Qt数据库编程
Qt网络编程
Linux
tcp
redis
mysql
网络
nginx
协程
io_uring
内存泄漏
bpf
ebpf
skynet
openresty
RocksDB
TiDB
ceph
etcd
fuse
p2p
http
mqtt
cuda
mutex
spinlock
hash
rbtree
btree
Makefile
git
wrk
Cuda
CUDA
D3D
d3d
ffmpeg
RTSP
WebRTC
PCM
RGB
YUv
MP4
FLV
TS
VLC
EasylCE
flvAnalyser
mp4box
audacity
Elecard
AAC
h264
SDL
AVFormat
AVCodec
AVPacket

View File

@@ -0,0 +1,7 @@
server:
ip: 0.0.0.0
port: 50054
accessToken: "ang1chubdev1ozhome256487d22sapguuv1ozhom"
log:
level: "info"
logPath: "runtime/logs/app.log"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
server:
ip: 0.0.0.0
port: 50053
accessToken: "ang1chubdev1ozhome256487d22sapguuv1ozhom"
log:
level: "info"
logPath: "runtime/logs/app.log"

View File

@@ -1,7 +1,8 @@
**/node_modules **/node_modules
*/node_modules */node_modules
node_modules node_modules
Dockerfile Dockerfile
.* .*
*/.* */.*
!.env !.env
!.env.production

View File

@@ -1,11 +1,11 @@
# Editor configuration, see http://editorconfig.org # Editor configuration, see http://editorconfig.org
root = true root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = tab indent_style = tab
indent_size = 2 indent_size = 2
end_of_line = lf end_of_line = lf
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true

12
ai-chat-web/.env.develop Normal file
View File

@@ -0,0 +1,12 @@
# Glob API URL
VITE_GLOB_API_URL=/api
VITE_APP_API_BASE_URL=http://localhost:7080/
# Whether long replies are supported, which may result in higher API fees
VITE_GLOB_OPEN_LONG_REPLY=false
# When you want to use PWA
VITE_GLOB_APP_PWA=false
VITE_USER_CENTER="http://localhost:8082?sys=ai"

View File

@@ -1,10 +1,8 @@
# Glob API URL VITE_GLOB_API_URL=/api
VITE_GLOB_API_URL=/api # Whether long replies are supported, which may result in higher API fees
VITE_GLOB_OPEN_LONG_REPLY=false
VITE_APP_API_BASE_URL=http://127.0.0.1:7080/
# When you want to use PWA
# Whether long replies are supported, which may result in higher API fees VITE_GLOB_APP_PWA=false
VITE_GLOB_OPEN_LONG_REPLY=false
VITE_USER_CENTER="https://user.0voice.com?sys=ai"
# When you want to use PWA
VITE_GLOB_APP_PWA=false

View File

@@ -1,17 +1,17 @@
"*.vue" eol=lf "*.vue" eol=lf
"*.js" eol=lf "*.js" eol=lf
"*.ts" eol=lf "*.ts" eol=lf
"*.jsx" eol=lf "*.jsx" eol=lf
"*.tsx" eol=lf "*.tsx" eol=lf
"*.cjs" eol=lf "*.cjs" eol=lf
"*.cts" eol=lf "*.cts" eol=lf
"*.mjs" eol=lf "*.mjs" eol=lf
"*.mts" eol=lf "*.mts" eol=lf
"*.json" eol=lf "*.json" eol=lf
"*.html" eol=lf "*.html" eol=lf
"*.css" eol=lf "*.css" eol=lf
"*.less" eol=lf "*.less" eol=lf
"*.scss" eol=lf "*.scss" eol=lf
"*.sass" eol=lf "*.sass" eol=lf
"*.styl" eol=lf "*.styl" eol=lf
"*.md" eol=lf "*.md" eol=lf

View File

@@ -0,0 +1,41 @@
name: build_docker
on:
push:
branches: [main]
release:
types: [created] # 表示在创建新的 Release 时触发
jobs:
build_docker:
name: Build docker
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- run: |
echo "本次构建的版本为:${GITHUB_REF_NAME} (但是这个变量目前上下文中无法获取到)"
echo 本次构建的版本为:${{ github.ref_name }}
env
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v4
with:
context: .
push: true
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/chatgpt-web:${{ github.ref_name }}
${{ secrets.DOCKERHUB_USERNAME }}/chatgpt-web:latest

47
ai-chat-web/.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set node
uses: actions/setup-node@v3
with:
node-version: 18.x
- name: Setup
run: npm i -g @antfu/ni
- name: Install
run: nci
- name: Lint
run: nr lint:fix
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set node
uses: actions/setup-node@v3
with:
node-version: 18.x
- name: Setup
run: npm i -g @antfu/ni
- name: Install
run: nci
- name: Typecheck
run: nr type-check

View File

@@ -1,32 +1,32 @@
# Logs # Logs
logs logs
*.log *.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
node_modules node_modules
.DS_Store .DS_Store
dist dist
dist-ssr dist-ssr
coverage coverage
*.local *.local
/cypress/videos/ /cypress/videos/
/cypress/screenshots/ /cypress/screenshots/
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/settings.json !.vscode/settings.json
!.vscode/extensions.json !.vscode/extensions.json
.idea .idea
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
# Environment variables files # Environment variables files
/service/.env /service/.env

View File

@@ -0,0 +1,59 @@
variables:
DEPLOY_ENV: ''
DEPLOY_IMG: ''
REPO: 'chatgpt-frontend'
SERVICE_NAME: 'chatgpt-stack_chatgpt-frontend'
workflow:
rules:
- if: $CI_COMMIT_BRANCH == "dev" && $CI_PIPELINE_SOURCE == "push"
variables:
DEPLOY_ENV: 'dev'
DEPLOY_IMG: "${REPO}:${CI_COMMIT_SHORT_SHA}"
- if: $CI_COMMIT_TAG
variables:
DEPLOY_ENV: 'prod'
DEPLOY_IMG: "${REPO}:${CI_COMMIT_TAG}"
- when: never
stages:
# 编译阶段
- build
# 部署阶段(部署到测试环境/部署到生产环境)
- deploy
build-job:
stage: build
tags:
- builder
before_script:
- docker login -u ${DOCKER_REGISTRY_USER} -p ${DOCKER_REGISTRY_PWD} ${DOCKER_REGISTRY}
script:
- docker build -t ${DOCKER_REGISTRY}/${DEPLOY_IMG} .
- docker push ${DOCKER_REGISTRY}/${DEPLOY_IMG}
deploy-dev-job:
only:
variables:
- $DEPLOY_ENV == "dev"
stage: deploy
tags:
- deployer
variables:
CONF_RM_STR: ""
before_script:
- docker login -u ${DOCKER_REGISTRY_USER} -p ${DOCKER_REGISTRY_PWD} ${DOCKER_REGISTRY}
script:
- docker service update ${SERVICE_NAME} --image ${DOCKER_REGISTRY}/${DEPLOY_IMG}
deploy-prod-job:
only:
variables:
- $DEPLOY_ENV == "prod"
stage: deploy
tags:
- deployer
variables:
CONF_RM_STR: ""
before_script:
- docker login -u ${DOCKER_REGISTRY_USER} -p ${DOCKER_REGISTRY_PWD} ${DOCKER_REGISTRY}
script:
- docker service update ${SERVICE_NAME} --image ${DOCKER_REGISTRY}/${DEPLOY_IMG}

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no -- commitlint --edit

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged

View File

@@ -1 +1 @@
strict-peer-dependencies=false strict-peer-dependencies=false

3
ai-chat-web/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "dbaeumer.vscode-eslint"]
}

65
ai-chat-web/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,65 @@
{
"prettier.enable": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"json",
"jsonc",
"json5",
"yaml",
"yml",
"markdown"
],
"cSpell.words": [
"antfu",
"axios",
"bumpp",
"chatgpt",
"chenzhaoyu",
"commitlint",
"davinci",
"dockerhub",
"esno",
"GPTAPI",
"highlightjs",
"hljs",
"iconify",
"katex",
"katexmath",
"linkify",
"logprobs",
"mdhljs",
"mila",
"nodata",
"OPENAI",
"pinia",
"Popconfirm",
"rushstack",
"Sider",
"tailwindcss",
"traptitech",
"tsup",
"Typecheck",
"unplugin",
"VITE",
"vueuse",
"Zhao"
],
"i18n-ally.enabledParsers": [
"ts"
],
"i18n-ally.sortKeys": true,
"i18n-ally.keepFulfilled": true,
"i18n-ally.localesPaths": [
"src/locales"
],
"i18n-ally.keystyle": "nested"
}

View File

@@ -1,57 +1,3 @@
## v2.11.0
`2023-04-26`
> [chatgpt-web-plus](https://github.com/Chanzhaoyu/chatgpt-web-plus) 新界面、完整用户管理
## Enhancement
- 更新默认 `accessToken` 反代地址为 [[pengzhile](https://github.com/pengzhile)] 的 `https://ai.fakeopen.com/api/conversation` [[24min](https://github.com/Chanzhaoyu/chatgpt-web/pull/1567/files)]
- 添加自定义 `temperature``top_p` [[quzard](https://github.com/Chanzhaoyu/chatgpt-web/pull/1260)]
- 优化代码 [[shunyue1320](https://github.com/Chanzhaoyu/chatgpt-web/pull/1328)]
- 优化复制代码反馈效果
## BugFix
- 修复余额查询和文案 [[luckywangxi](https://github.com/Chanzhaoyu/chatgpt-web/pull/1174)][[zuoning777](https://github.com/Chanzhaoyu/chatgpt-web/pull/1296)]
- 修复默认语言错误 [[idawnwon](https://github.com/Chanzhaoyu/chatgpt-web/pull/1352)]
- 修复 `onRegenerate` 下问题 [[leafsummer](https://github.com/Chanzhaoyu/chatgpt-web/pull/1188)]
## Other
- 引导用户触发提示词 [[RyanXinOne](https://github.com/Chanzhaoyu/chatgpt-web/pull/1183)]
- 添加韩语翻译 [[Kamilake](https://github.com/Chanzhaoyu/chatgpt-web/pull/1372)]
- 添加俄语翻译 [[aquaratixc](https://github.com/Chanzhaoyu/chatgpt-web/pull/1571)]
- 优化翻译和文本检查 [[PeterDaveHello](https://github.com/Chanzhaoyu/chatgpt-web/pull/1460)]
- 移除无用文件
## v2.10.9
`2023-04-03`
> 更新默认 `accessToken` 反代地址为 [[pengzhile](https://github.com/pengzhile)] 的 `https://ai.fakeopen.com/api/conversation`
## Enhancement
- 添加 `socks5` 代理认证 [[yimiaoxiehou](https://github.com/Chanzhaoyu/chatgpt-web/pull/999)]
- 添加 `socks` 代理用户名密码的配置 [[hank-cp](https://github.com/Chanzhaoyu/chatgpt-web/pull/890)]
- 添加可选日志打印 [[zcong1993](https://github.com/Chanzhaoyu/chatgpt-web/pull/1041)]
- 更新侧边栏按钮本地化[[simonwu53](https://github.com/Chanzhaoyu/chatgpt-web/pull/911)]
- 优化代码块滚动条高度 [[Fog3211](https://github.com/Chanzhaoyu/chatgpt-web/pull/1153)]
## BugFix
- 修复 `PWA` 问题 [[bingo235](https://github.com/Chanzhaoyu/chatgpt-web/pull/807)]
- 修复 `ESM` 错误 [[kidonng](https://github.com/Chanzhaoyu/chatgpt-web/pull/826)]
- 修复反向代理开启时限流失效的问题 [[gitgitgogogo](https://github.com/Chanzhaoyu/chatgpt-web/pull/863)]
- 修复 `docker` 构建时 `.env` 可能被忽略的问题 [[zaiMoe](https://github.com/Chanzhaoyu/chatgpt-web/pull/877)]
- 修复导出异常错误 [[KingTwinkle](https://github.com/Chanzhaoyu/chatgpt-web/pull/938)]
- 修复空值异常 [[vchenpeng](https://github.com/Chanzhaoyu/chatgpt-web/pull/1103)]
- 移动端上的体验问题
## Other
- `Docker` 容器名字名义 [[LOVECHEN](https://github.com/Chanzhaoyu/chatgpt-web/pull/1035)]
- `kubernetes` 部署配置 [[CaoYunzhou](https://github.com/Chanzhaoyu/chatgpt-web/pull/1001)]
- 感谢 [[assassinliujie](https://github.com/Chanzhaoyu/chatgpt-web/pull/962)] 和 [[puppywang](https://github.com/Chanzhaoyu/chatgpt-web/pull/1017)] 的某些贡献
- 更新 `kubernetes/deploy.yaml` [[idawnwon](https://github.com/Chanzhaoyu/chatgpt-web/pull/1085)]
- 文档更新 [[#yi-ge](https://github.com/Chanzhaoyu/chatgpt-web/pull/883)]
- 文档更新 [[weifeng12x](https://github.com/Chanzhaoyu/chatgpt-web/pull/880)]
- 依赖更新
## v2.10.8 ## v2.10.8
`2023-03-23` `2023-03-23`
@@ -125,7 +71,7 @@
`2023-03-13` `2023-03-13`
更新依赖,`access_token` 默认代理为 [pengzhile](https://github.com/pengzhile) 的 `https://bypass.duti.tech/api/conversation` 更新依赖,`access_token` 默认代理为 [acheong08](https://github.com/acheong08) 的 `https://bypass.duti.tech/api/conversation`
## Feature ## Feature
- `Prompt` 商店在线导入可以导入两种 `recommend.json`里提到的模板 [simonwu53](https://github.com/Chanzhaoyu/chatgpt-web/pull/521) - `Prompt` 商店在线导入可以导入两种 `recommend.json`里提到的模板 [simonwu53](https://github.com/Chanzhaoyu/chatgpt-web/pull/521)

15
ai-chat-web/Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM quay.io/0voice/node:lts-alpine AS frontend
RUN npm install pnpm -g
WORKDIR /app
COPY package.json pnpm-lock.yaml .npmrc ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm run build-only
FROM quay.io/0voice/nginx:1.25.4 AS web
COPY ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=frontend /app/dist/ /usr/share/nginx/html/
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -0,0 +1,64 @@
# ai-chat-web
## node 安装
### windows
1. 上[官网](https://nodejs.org/en)下载18.16.0 LTS版本
2. 查看node 是否安装成功
```
node -v
npm -v
```
3. 安装pnpm
```
npm install pnpm -g
```
### ubuntu
1. 设置 apt 源,设置后可查看/etc/apt/sources.list.d/nodesource.list 文件
```
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
```
2. 安装nodejs
```
sudo apt-get install -y nodejs
```
3. 验证
```
node -v
npm -v
```
4. 安装pnpm
```
sudo npm install pnpm -g
```
## 编译运行
1. 依赖安装
```
pnpm bootstrap
```
2. 本地运行
```
pnpm dev
```
3. 打包发布版本
```
pnpm build-only
```
## 提交代码的规则
```
* commitlint 规则是指在提交代码时要遵循的规范,常见的 commitlint 规则如下:
* type用于说明 commit 的类型,例如 feat新功能、fix修复 bug、docs文档更新、style样式修改、refactor重构代码等。
* scope用于说明 commit 影响的范围,例如组件、模块、页面等。
* subject用于简短地描述 commit 的内容,建议不超过 50 个字符。
* body用于详细描述 commit 的改动内容,可以分成多行。
* footer用于关闭 issue 或者添加相关链接等信息。
* 长度限制commit message 不应该过长,一般不超过 72 个字符。
```

View File

@@ -0,0 +1 @@
export * from './proxy'

View File

@@ -0,0 +1,16 @@
import type { ProxyOptions } from 'vite'
export function createViteProxy(isOpenProxy: boolean, viteEnv: ImportMetaEnv) {
if (!isOpenProxy)
return
const proxy: Record<string, string | ProxyOptions> = {
'/api': {
target: viteEnv.VITE_APP_API_BASE_URL,
changeOrigin: true,
rewrite: path => path.replace('/api/', '/'),
},
}
return proxy
}

View File

@@ -1,47 +1,41 @@
version: '3' version: '3'
services: services:
app: app:
container_name: chatgpt-web image: chenzhaoyu94/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可
image: chenzhaoyu94/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可 ports:
ports: - 3002:3002
- 3002:3002 environment:
environment: # 二选一
# 二选一 OPENAI_API_KEY: sk-xxx
OPENAI_API_KEY: # 二选一
# 二选一 OPENAI_ACCESS_TOKEN: xxx
OPENAI_ACCESS_TOKEN: # API接口地址可选设置 OPENAI_API_KEY 时可用
# API接口地址可选设置 OPENAI_API_KEY 时可用 OPENAI_API_BASE_URL: xxx
OPENAI_API_BASE_URL: # API模型可选设置 OPENAI_API_KEY 时可用
# API模型可选设置 OPENAI_API_KEY 时可用 OPENAI_API_MODEL: xxx
OPENAI_API_MODEL: # 反向代理,可选
# 反向代理,可选 API_REVERSE_PROXY: xxx
API_REVERSE_PROXY: # 访问权限密钥,可选
# 访问权限密钥,可选 AUTH_SECRET_KEY: xxx
AUTH_SECRET_KEY: # 每小时最大请求次数,可选,默认无限
# 每小时最大请求次数,可选,默认无限 MAX_REQUEST_PER_HOUR: 0
MAX_REQUEST_PER_HOUR: 0 # 超时,单位毫秒,可选
# 超时,单位毫秒,可选 TIMEOUT_MS: 60000
TIMEOUT_MS: 60000 # Socks代理可选和 SOCKS_PROXY_PORT 一起时生效
# Socks代理可选和 SOCKS_PROXY_PORT 一起时生效 SOCKS_PROXY_HOST: xxx
SOCKS_PROXY_HOST: # Socks代理端口可选SOCKS_PROXY_HOST 一起时生效
# Socks代理端口可选和 SOCKS_PROXY_HOST 一起时生效 SOCKS_PROXY_PORT: xxx
SOCKS_PROXY_PORT: # HTTPS_PROXY 代理,可选
# Socks代理用户名可选和 SOCKS_PROXY_HOST & SOCKS_PROXY_PORT 一起时生效 HTTPS_PROXY: http://xxx:7890
SOCKS_PROXY_USERNAME: nginx:
# Socks代理密码可选和 SOCKS_PROXY_HOST & SOCKS_PROXY_PORT 一起时生效 image: nginx:alpine
SOCKS_PROXY_PASSWORD: ports:
# HTTPS_PROXY 代理,可选 - '80:80'
HTTPS_PROXY: expose:
nginx: - '80'
container_name: nginx volumes:
image: nginx:alpine - ./nginx/html:/usr/share/nginx/html
ports: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
- '80:80' links:
expose: - app
- '80'
volumes:
- ./nginx/html:/usr/share/nginx/html
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
links:
- app

View File

@@ -1,27 +1,20 @@
server { server {
listen 80; listen 80;
server_name localhost; server_name localhost;
charset utf-8; charset utf-8;
error_page 500 502 503 504 /50x.html; error_page 500 502 503 504 /50x.html;
location / {
# 防止爬虫抓取 root /usr/share/nginx/html;
if ($http_user_agent ~* "360Spider|JikeSpider|Spider|spider|bot|Bot|2345Explorer|curl|wget|webZIP|qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot|NSPlayer|bingbot") try_files $uri /index.html;
{ }
return 403;
} location /api {
proxy_set_header X-Real-IP $remote_addr; #转发用户IP
location / { proxy_pass http://app:3002;
root /usr/share/nginx/html; }
try_files $uri /index.html;
} proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
location /api { proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Real-IP $remote_addr; #转发用户IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://app:3002; }
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

View File

@@ -15,6 +15,6 @@ server {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://chatgpt-web-backend:7080/api/; proxy_pass http://ai-chat-backend:7080/api/;
} }
} }

View File

@@ -2,12 +2,12 @@
<html lang="zh-cmn-Hans"> <html lang="zh-cmn-Hans">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="/favicon.svg"> <link rel="icon" type="image/svg+xml" href="/favicon.jpg">
<meta content="yes" name="apple-mobile-web-app-capable"/> <meta content="yes" name="apple-mobile-web-app-capable"/>
<link rel="apple-touch-icon" href="/favicon.ico"> <link rel="apple-touch-icon" href="/favicon.ico">
<meta name="viewport" <meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" /> content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
<title>ChatGPT Web</title> <title>零声教学AI助手公测</title>
</head> </head>
<body class="dark:bg-black"> <body class="dark:bg-black">
@@ -79,5 +79,14 @@
</div> </div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
<script>
var _hmt = _hmt || [];
(function () {
const hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?29783f1f3a946661c3d41e96752536d6'
const s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
})()
</script>
</html> </html>

View File

@@ -1,21 +1,21 @@
MIT License MIT License
Copyright (c) 2023 ChenZhaoYu Copyright (c) 2023 ChenZhaoYu
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

View File

@@ -1,8 +1,8 @@
{ {
"name": "chatgpt-web", "name": "chatgpt-web",
"version": "2.11.0", "version": "2.10.8",
"private": false, "private": false,
"description": "ChatGPT Web", "description": "零声教学AI助手",
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>", "author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
"keywords": [ "keywords": [
"chatgpt-web", "chatgpt-web",
@@ -11,10 +11,10 @@
"vue" "vue"
], ],
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --mode develop",
"build": "run-p type-check build-only", "build": "run-p type-check build-only",
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build --mode production",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",

6903
ai-chat-web/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,6 +1,6 @@
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios' import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
import { post } from '@/utils/request' import { post } from '@/utils/request'
import { useAuthStore, useSettingStore } from '@/store' import { useSettingStore } from '@/store'
export function fetchChatAPI<T = any>( export function fetchChatAPI<T = any>(
prompt: string, prompt: string,
@@ -28,25 +28,10 @@ export function fetchChatAPIProcess<T = any>(
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void }, onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
) { ) {
const settingStore = useSettingStore() const settingStore = useSettingStore()
const authStore = useAuthStore()
let data: Record<string, any> = {
prompt: params.prompt,
options: params.options,
}
if (authStore.isChatGPTAPI) {
data = {
...data,
systemMessage: settingStore.systemMessage,
temperature: settingStore.temperature,
top_p: settingStore.top_p,
}
}
return post<T>({ return post<T>({
url: '/chat-process', url: '/chat-process',
data, data: { prompt: params.prompt, options: params.options, systemMessage: settingStore.systemMessage },
signal: params.signal, signal: params.signal,
onDownloadProgress: params.onDownloadProgress, onDownloadProgress: params.onDownloadProgress,
}) })
@@ -64,3 +49,17 @@ export function fetchVerify<T>(token: string) {
data: { token }, data: { token },
}) })
} }
export function fetchCode<T>(phone: string) {
return post<T>({
url: '/v1/sms/send/code',
data: { phone },
})
}
export function login<T>(phone: string, code: string) {
return post<T>({
url: '/v1/user/login',
data: { user_name: phone, pwd: code, type: 1 },
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -147,7 +147,7 @@ const clearPromptTemplate = () => {
message.success(t('common.clearSuccess')) message.success(t('common.clearSuccess'))
} }
const importPromptTemplate = (from = 'online') => { const importPromptTemplate = () => {
try { try {
const jsonData = JSON.parse(tempPromptValue.value) const jsonData = JSON.parse(tempPromptValue.value)
let key = '' let key = ''
@@ -168,7 +168,7 @@ const importPromptTemplate = (from = 'online') => {
} }
for (const i of jsonData) { for (const i of jsonData) {
if (!(key in i) || !(value in i)) if (!('key' in i) || !('value' in i))
throw new Error(t('store.importError')) throw new Error(t('store.importError'))
let safe = true let safe = true
for (const j of promptList.value) { for (const j of promptList.value) {
@@ -191,8 +191,6 @@ const importPromptTemplate = (from = 'online') => {
catch { catch {
message.error('JSON 格式错误,请检查 JSON 格式') message.error('JSON 格式错误,请检查 JSON 格式')
} }
if (from === 'local')
showModal.value = !showModal.value
} }
// //
@@ -471,7 +469,7 @@ const dataSource = computed(() => {
block block
type="primary" type="primary"
:disabled="inputStatus" :disabled="inputStatus"
@click="() => { importPromptTemplate('local') }" @click="() => { importPromptTemplate() }"
> >
{{ t('common.import') }} {{ t('common.import') }}
</NButton> </NButton>

View File

@@ -11,7 +11,7 @@ interface ConfigState {
apiModel?: string apiModel?: string
socksProxy?: string socksProxy?: string
httpsProxy?: string httpsProxy?: string
usage?: string balance?: string
} }
const authStore = useAuthStore() const authStore = useAuthStore()
@@ -52,17 +52,17 @@ onMounted(() => {
href="https://github.com/Chanzhaoyu/chatgpt-web" href="https://github.com/Chanzhaoyu/chatgpt-web"
target="_blank" target="_blank"
> >
GitHub Github
</a> </a>
免费且基于 MIT 协议没有任何形式的付费行为 免费且基于 MIT 协议没有任何形式的付费行为
</p> </p>
<p> <p>
如果你觉得此项目对你有帮助请在 GitHub 帮我点个 Star 或者给予一点赞助谢谢 如果你觉得此项目对你有帮助请在 Github 帮我点个 Star 或者给予一点赞助谢谢
</p> </p>
</div> </div>
<p>{{ $t("setting.api") }}{{ config?.apiModel ?? '-' }}</p> <p>{{ $t("setting.api") }}{{ config?.apiModel ?? '-' }}</p>
<p v-if="isChatGPTAPI"> <p v-if="isChatGPTAPI">
{{ $t("setting.monthlyUsage") }}{{ config?.usage ?? '-' }} {{ $t("setting.balance") }}{{ config?.balance ?? '-' }}
</p> </p>
<p v-if="!isChatGPTAPI"> <p v-if="!isChatGPTAPI">
{{ $t("setting.reverseProxy") }}{{ config?.reverseProxy ?? '-' }} {{ $t("setting.reverseProxy") }}{{ config?.reverseProxy ?? '-' }}

View File

@@ -0,0 +1,46 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { NButton, NInput, useMessage } from 'naive-ui'
import { useSettingStore } from '@/store'
import type { SettingsState } from '@/store/modules/settings/helper'
import { t } from '@/locales'
const settingStore = useSettingStore()
const ms = useMessage()
const systemMessage = ref(settingStore.systemMessage ?? '')
function updateSettings(options: Partial<SettingsState>) {
settingStore.updateSetting(options)
ms.success(t('common.success'))
}
function handleReset() {
settingStore.resetSetting()
ms.success(t('common.success'))
window.location.reload()
}
</script>
<template>
<div class="p-4 space-y-5 min-h-[200px]">
<div class="space-y-6">
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.role') }}</span>
<div class="flex-1">
<NInput v-model:value="systemMessage" placeholder="" />
</div>
<NButton size="tiny" text type="primary" @click="updateSettings({ systemMessage })">
{{ $t('common.save') }}
</NButton>
</div>
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">&nbsp;</span>
<NButton size="small" @click="handleReset">
{{ $t('common.reset') }}
</NButton>
</div>
</div>
</div>
</template>

View File

@@ -57,8 +57,6 @@ const languageOptions: { label: string; key: Language; value: Language }[] = [
{ label: '简体中文', key: 'zh-CN', value: 'zh-CN' }, { label: '简体中文', key: 'zh-CN', value: 'zh-CN' },
{ label: '繁體中文', key: 'zh-TW', value: 'zh-TW' }, { label: '繁體中文', key: 'zh-TW', value: 'zh-TW' },
{ label: 'English', key: 'en-US', value: 'en-US' }, { label: 'English', key: 'en-US', value: 'en-US' },
{ label: '한국어', key: 'ko-KR', value: 'ko-KR' },
{ label: 'Русский язык', key: 'ru-RU', value: 'ru-RU' },
] ]
function updateUserInfo(options: Partial<UserInfo>) { function updateUserInfo(options: Partial<UserInfo>) {

View File

@@ -1,13 +1,12 @@
<script setup lang='ts'> <script setup lang='ts'>
import { computed, useAttrs } from 'vue' import { computed, useAttrs } from 'vue'
import { Icon } from '@iconify/vue' import { Icon } from '@iconify/vue'
import type { IconifyIcon } from '@iconify/vue'
interface Props { interface Props {
icon?: string | IconifyIcon icon?: string
} }
const props = defineProps<Props>() defineProps<Props>()
const attrs = useAttrs() const attrs = useAttrs()
@@ -18,5 +17,5 @@ const bindAttrs = computed<{ class: string; style: string }>(() => ({
</script> </script>
<template> <template>
<Icon :icon="props.icon ?? ''" v-bind="bindAttrs" /> <Icon :icon="icon" v-bind="bindAttrs" />
</template> </template>

View File

@@ -1,5 +1,5 @@
import { computed } from 'vue' import { computed } from 'vue'
import { enUS, koKR, zhCN, zhTW } from 'naive-ui' import { enUS, zhCN, zhTW } from 'naive-ui'
import { useAppStore } from '@/store' import { useAppStore } from '@/store'
import { setLocale } from '@/locales' import { setLocale } from '@/locales'
@@ -11,12 +11,6 @@ export function useLanguage() {
case 'en-US': case 'en-US':
setLocale('en-US') setLocale('en-US')
return enUS return enUS
case 'ru-RU':
setLocale('ru-RU')
return enUS
case 'ko-KR':
setLocale('ko-KR')
return koKR
case 'zh-CN': case 'zh-CN':
setLocale('zh-CN') setLocale('zh-CN')
return zhCN return zhCN
@@ -25,7 +19,7 @@ export function useLanguage() {
return zhTW return zhTW
default: default:
setLocale('zh-CN') setLocale('zh-CN')
return zhCN return enUS
} }
}) })

View File

@@ -9,10 +9,11 @@ export function useTheme() {
const OsTheme = useOsTheme() const OsTheme = useOsTheme()
const isDark = computed(() => { const isDark = computed(() => {
if (appStore.theme === 'auto') return true
return OsTheme.value === 'dark' // if (appStore.theme === 'auto')
else // return OsTheme.value === 'dark'
return appStore.theme === 'dark' // else
// return appStore.theme === 'dark'
}) })
const theme = computed(() => { const theme = computed(() => {

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -28,8 +28,7 @@ export default {
unauthorizedTips: 'Unauthorized, please verify first.', unauthorizedTips: 'Unauthorized, please verify first.',
}, },
chat: { chat: {
newChatButton: 'New Chat', placeholder: 'Ask me anything...(Shift + Enter = line break)',
placeholder: 'Ask me anything...(Shift + Enter = line break, "/" to trigger prompts)',
placeholderMobile: 'Ask me anything...', placeholderMobile: 'Ask me anything...',
copy: 'Copy', copy: 'Copy',
copied: 'Copied', copied: 'Copied',
@@ -59,8 +58,6 @@ export default {
name: 'Name', name: 'Name',
description: 'Description', description: 'Description',
role: 'Role', role: 'Role',
temperature: 'Temperature',
top_p: 'Top_p',
resetUserInfo: 'Reset UserInfo', resetUserInfo: 'Reset UserInfo',
chatHistory: 'ChatHistory', chatHistory: 'ChatHistory',
theme: 'Theme', theme: 'Theme',
@@ -71,10 +68,8 @@ export default {
socks: 'Socks', socks: 'Socks',
httpsProxy: 'HTTPS Proxy', httpsProxy: 'HTTPS Proxy',
balance: 'API Balance', balance: 'API Balance',
monthlyUsage: 'Monthly Usage',
}, },
store: { store: {
siderButton: 'Prompt Store',
local: 'Local', local: 'Local',
online: 'Online', online: 'Online',
title: 'Title', title: 'Title',

View File

@@ -1,10 +1,8 @@
import type { App } from 'vue' import type { App } from 'vue'
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
import enUS from './en-US' import enUS from './en-US'
import koKR from './ko-KR'
import zhCN from './zh-CN' import zhCN from './zh-CN'
import zhTW from './zh-TW' import zhTW from './zh-TW'
import ruRU from './ru-RU'
import { useAppStoreWithOut } from '@/store/modules/app' import { useAppStoreWithOut } from '@/store/modules/app'
import type { Language } from '@/store/modules/app/helper' import type { Language } from '@/store/modules/app/helper'
@@ -18,10 +16,8 @@ const i18n = createI18n({
allowComposition: true, allowComposition: true,
messages: { messages: {
'en-US': enUS, 'en-US': enUS,
'ko-KR': koKR,
'zh-CN': zhCN, 'zh-CN': zhCN,
'zh-TW': zhTW, 'zh-TW': zhTW,
'ru-RU': ruRU,
}, },
}) })

View File

@@ -24,12 +24,11 @@ export default {
wrong: '好像出错了,请稍后再试。', wrong: '好像出错了,请稍后再试。',
success: '操作成功', success: '操作成功',
failed: '操作失败', failed: '操作失败',
verify: '验证', verify: '登录',
unauthorizedTips: '未经授权,请先进行验证。', unauthorizedTips: '未经授权,请先进行验证。',
}, },
chat: { chat: {
newChatButton: '新建聊天', placeholder: '来说点什么吧...Shift + Enter = 换行)',
placeholder: '来说点什么吧...Shift + Enter = 换行,"/" 触发提示词)',
placeholderMobile: '来说点什么...', placeholderMobile: '来说点什么...',
copy: '复制', copy: '复制',
copied: '复制成功', copied: '复制成功',
@@ -59,8 +58,6 @@ export default {
name: '名称', name: '名称',
description: '描述', description: '描述',
role: '角色设定', role: '角色设定',
temperature: 'Temperature',
top_p: 'Top_p',
resetUserInfo: '重置用户信息', resetUserInfo: '重置用户信息',
chatHistory: '聊天记录', chatHistory: '聊天记录',
theme: '主题', theme: '主题',
@@ -71,10 +68,8 @@ export default {
socks: 'Socks', socks: 'Socks',
httpsProxy: 'HTTPS Proxy', httpsProxy: 'HTTPS Proxy',
balance: 'API余额', balance: 'API余额',
monthlyUsage: '本月使用量',
}, },
store: { store: {
siderButton: '提示词商店',
local: '本地', local: '本地',
online: '在线', online: '在线',
title: '标题', title: '标题',

View File

@@ -28,8 +28,7 @@ export default {
unauthorizedTips: '未經授權,請先進行驗證。', unauthorizedTips: '未經授權,請先進行驗證。',
}, },
chat: { chat: {
newChatButton: '新增對話', placeholder: '來說點什麼...Shift + Enter = 換行)',
placeholder: '來說點什麼...Shift + Enter = 換行,"/" 觸發提示詞)',
placeholderMobile: '來說點什麼...', placeholderMobile: '來說點什麼...',
copy: '複製', copy: '複製',
copied: '複製成功', copied: '複製成功',
@@ -53,14 +52,12 @@ export default {
setting: { setting: {
setting: '設定', setting: '設定',
general: '總覽', general: '總覽',
advanced: '進階', advanced: '高級',
config: '設定', config: '設定',
avatarLink: '頭貼連結', avatarLink: '頭貼連結',
name: '名稱', name: '名稱',
description: '描述', description: '描述',
role: '角色設定', role: '角色設定',
temperature: 'Temperature',
top_p: 'Top_p',
resetUserInfo: '重設使用者資訊', resetUserInfo: '重設使用者資訊',
chatHistory: '紀錄', chatHistory: '紀錄',
theme: '主題', theme: '主題',
@@ -70,11 +67,9 @@ export default {
timeout: '逾時', timeout: '逾時',
socks: 'Socks', socks: 'Socks',
httpsProxy: 'HTTPS Proxy', httpsProxy: 'HTTPS Proxy',
balance: 'API Credit 餘額', balance: 'API額',
monthlyUsage: '本月使用量',
}, },
store: { store: {
siderButton: '提示詞商店',
local: '本機', local: '本機',
online: '線上', online: '線上',
title: '標題', title: '標題',

View File

@@ -0,0 +1,30 @@
import type { Router } from 'vue-router'
import { useAuthStoreWithout } from '@/store/modules/auth'
export function setupPageGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStoreWithout()
if (!authStore.session) {
// try {
// const data = await authStore.getSession()
// if (String(data.auth) === 'false' && authStore.token)
// authStore.removeToken()
// if (to.path === '/500')
// next({ name: 'Root' })
// else
// next()
// }
// catch (error) {
// if (to.path !== '/500')
// next({ name: '500' })
// else
// next()
// }
next()
}
else {
next()
}
})
}

View File

@@ -4,7 +4,7 @@ const LOCAL_NAME = 'appSetting'
export type Theme = 'light' | 'dark' | 'auto' export type Theme = 'light' | 'dark' | 'auto'
export type Language = 'zh-CN' | 'zh-TW' | 'en-US' | 'ko-KR' | 'ru-RU' export type Language = 'zh-CN' | 'zh-TW' | 'en-US'
export interface AppState { export interface AppState {
siderCollapsed: boolean siderCollapsed: boolean

View File

@@ -2,12 +2,7 @@ import { ss } from '@/utils/storage'
const LOCAL_NAME = 'promptStore' const LOCAL_NAME = 'promptStore'
export interface PromptItem { export type PromptList = []
key: string
value: string
}
export type PromptList = PromptItem[]
export interface PromptStore { export interface PromptStore {
promptList: PromptList promptList: PromptList

View File

@@ -1,12 +1,12 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import type { PromptList, PromptStore } from './helper' import type { PromptStore } from './helper'
import { getLocalPromptList, setLocalPromptList } from './helper' import { getLocalPromptList, setLocalPromptList } from './helper'
export const usePromptStore = defineStore('prompt-store', { export const usePromptStore = defineStore('prompt-store', {
state: (): PromptStore => getLocalPromptList(), state: (): PromptStore => getLocalPromptList(),
actions: { actions: {
updatePromptList(promptList: PromptList) { updatePromptList(promptList: []) {
this.$patch({ promptList }) this.$patch({ promptList })
setLocalPromptList({ promptList }) setLocalPromptList({ promptList })
}, },

View File

@@ -4,15 +4,12 @@ const LOCAL_NAME = 'settingsStorage'
export interface SettingsState { export interface SettingsState {
systemMessage: string systemMessage: string
temperature: number
top_p: number
} }
export function defaultSetting(): SettingsState { export function defaultSetting(): SettingsState {
const currentDate = new Date().toISOString().split('T')[0]
return { return {
systemMessage: 'You are ChatGPT, a large language model trained by OpenAI. Follow the user\'s instructions carefully. Respond using markdown.', systemMessage: `You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.\nKnowledge cutoff: 2021-09-01\nCurrent date: ${currentDate}`,
temperature: 0.8,
top_p: 1,
} }
} }

Some files were not shown because too many files have changed in this diff Show More