Skip to content

Commit

Permalink
新增 API
Browse files Browse the repository at this point in the history
  • Loading branch information
dairoot committed Nov 9, 2024
1 parent 93e3a2f commit e39f220
Show file tree
Hide file tree
Showing 23 changed files with 498 additions and 74 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ TZ=Asia/Shanghai # 设置时区

ADMIN_USERNAME=dairoot # 管理后台账号密码
ADMIN_PASSWORD=dairoot # 管理后台账号密码

ALLOW_REGISTER=true # 是否允许用户注册
# PROXY_URL_POOL=http://username@password@ip:port,socks5://username@password@ip:port # 代理池地址


103 changes: 96 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ https://github.com/user-attachments/assets/7b868672-cfaf-430c-9ec4-f1617a428225
</a>
-->

### 1. 部署
打开命令行终端,执行以下命令

```bash
# 切换到 home 目录,并克隆 ChatGPT-Mirror 仓库
Expand All @@ -43,18 +43,107 @@ cp .env.example .env && vi .env
# 启动
./deploy.sh

访问 http://localhost:50002
```
访问 http://127.0.0.1:50002 或访问 http://外网ip:50002


配置域名,请点击查看[完整部署流程](./docs/deploy.md)


## 环境变量

| 字段 | 类型 | 默认值 | 必填 | 描述 |
| ---------------- | --------- | ------- | ---- | ------------------------------------------------------------------------------------------------------- |
| `ADMIN_USERNAME` | `string` | `None` | `` | 管理后台账号 |
| `ADMIN_PASSWORD` | `string` | `None` | `` | 管理后台密码 |
| `PROXY_URL_POOL` | `string` | `None` | `` | 代理池链接,多个代理用逗号分隔<br>`http://username@password@ip:port,socks5://username@password@ip:port` |
<table>
<tr align="left">
<th>分类</th>
<th>变量名</th>
<th>类型</th>
<th>默认值</th>
<th>描述</th>
</tr>
<tr align="left">
<td rowspan="4">管理后台</td>
<td><code>ADMIN_USERNAME</code></td>
<td><code>String</code></td>
<td><code>None</code></td>
<td>管理后台账号</td>
</tr>
<tr align="left">
<td><code>ADMIN_PASSWORD</code></td>
<td><code>String</code></td>
<td><code>None</code></td>
<td>管理后台密码</td>
</tr>
<tr align="left">
<td><code>ALLOW_REGISTER</code></td>
<td><code>Boolean</code></td>
<td><code>true</code></td>
<td>是否允许注册</td>
</tr>
</tr>
<tr align="left">
<td><code>USE_SERVER_RENDER</code></td>
<td><code>Boolean</code></td>
<td><code>true</code></td>
<td>服务端托管 Proofofwork</td>
</tr>
<tr align="left">
<td rowspan="3">API 相关</td>
<td><code>ENABLE_MIRROR_API</code></td>
<td><code>Boolean</code></td>
<td><code>true</code></td>
<td>是否开启 API 访问</td>
</tr>
<tr align="left">
<td><code>MIRROR_API_PREFIX</code></td>
<td><code>String</code></td>
<td><code>None</code></td>
<td>API 访问前缀,建议配置</td>
</tr>
<tr align="left">
<td><code>HATD</code></td>
<td><code>Boolean</code></td>
<td><code>false</code></td>
<td>开启临时聊天(不保存聊天记录)</td>
</tr>
<tr align="left">
<td rowspan="2">系统变量</td>
<td><code>PROXY_URL_POOL</code></td>
<td><code>String</code></td>
<td><code>None</code></td>
<td>代理池链接,多个代理用逗号分隔<br><code>http://username@password@ip:port,</code><br/><code>socks5://username@password@ip:port</code></td>
</tr>
</table>

# 聊天 API 接口

可搭配 [ChatGPT-Next-Web](https://app.nextchat.dev)[Lobe-Chat](https://github.com/lobehub/lobe-chat) 使用
```
accessToken 获取地址:https://chatgpt.com/api/auth/session
API 地址为:https://你的地址
```

聊天接口请求示例:

```bash
export accessToken=XXXXX # 获取地址:https://chatgpt.com/api/auth/session
export yourUrl=http://127.0.0.1:50002


curl --location "${yourUrl}/v1/chat/completions" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${accessToken}" \
--data '{
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "你好呀!"}],
"stream": true,
"conversation_id": null,
"parent_message_id": null,
"hatd": false
}'
```

更多 API 请点击查看:[高阶玩法](./docs/chatapi-gateway.md)

## FQA

Expand Down
13 changes: 13 additions & 0 deletions backend/app/accounts/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework import serializers

from app.accounts.models import User, VisitLog
from app.chatgpt.models import ChatgptAccount


class ShowVisitLogModelSerializer(serializers.ModelSerializer):
Expand All @@ -12,6 +13,18 @@ class Meta:
class ShowUserAccountModelSerializer(serializers.ModelSerializer):
last_login = serializers.DateTimeField(format="%Y-%m-%d %H:%M")
date_joined = serializers.DateTimeField(format="%Y-%m-%d %H:%M")
use_count = serializers.SerializerMethodField()
chatgpt_count = serializers.SerializerMethodField()

def __init__(self, *args, use_count_dict=dict, **kwargs):
super().__init__(*args, **kwargs)
self.use_count_dict = use_count_dict

def get_chatgpt_count(self, obj):
return ChatgptAccount.get_by_gptcar_list(obj.gptcar_list).count()

def get_use_count(self, obj):
return self.use_count_dict.get(obj.username, 0)

class Meta:
model = User
Expand Down
3 changes: 2 additions & 1 deletion backend/app/accounts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
from django.urls import path

from app.accounts.views import UserAccountView, UserRelateGPTCarView, VisitLogView, BatchModelLimit, \
UserChatGPTAccountList
UserChatGPTAccountList, GetMirrorToken
from app.accounts.views.login import AccountLogin, UserFreeLoginView, AccountRegister

urlpatterns = [
path("", UserAccountView.as_view()),
path("get-mirror-token", GetMirrorToken.as_view()),
path("register", AccountRegister.as_view()),
path("login-free", UserFreeLoginView.as_view()),
path("chatgpt-list", UserChatGPTAccountList.as_view()),
Expand Down
50 changes: 37 additions & 13 deletions backend/app/accounts/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,42 @@
from app.accounts.models import User, VisitLog
from app.accounts.serializers import ShowVisitLogModelSerializer, AddUserAccountSerializer, UserBindChatGPTSerializer, \
ShowUserAccountModelSerializer, BatchModelLimitSerializer
from app.chatgpt.models import ChatgptAccount, ChatgptCar
from app.chatgpt.models import ChatgptAccount
from app.page import DefaultPageNumberPagination
from app.settings import ADMIN_USERNAME
from app.utils import req_gateway


class GetMirrorToken(APIView):
permission_classes = (IsAuthenticated,)

def get(self, request):
user = User.objects.filter(id=request.GET["user_id"]).first()

user_account_list = ChatgptAccount.get_by_gptcar_list(user.gptcar_list)
chatgpt_username_list = [i.chatgpt_username for i in user_account_list]
res = req_gateway("post", "/api/get-mirror-token", json={
"chatgpt_list": chatgpt_username_list,
"user_name": user.username,
})
for line in res:
obj = ChatgptAccount.objects.filter(chatgpt_username=line["chatgpt_username"]).first()
line["auth_status"] = obj.auth_status
return Response(res)


class UserChatGPTAccountList(APIView):
permission_classes = (IsAuthenticated,)

def get(self, request):
results = []
chatgpt_account_list = []
for line in ChatgptCar.objects.filter(id__in=request.user.gptcar_list).values("gpt_account_list"):
chatgpt_account_list.extend(line["gpt_account_list"])

gptaccount = ChatgptAccount.objects.filter(auth_status=True)
if chatgpt_account_list:
gptaccount = gptaccount.filter(id__in=chatgpt_account_list)
for line in gptaccount.all():
user_account_list = ChatgptAccount.get_by_gptcar_list(request.user.gptcar_list)
for line in user_account_list:
results.append({
"id": line.id,
"chatgpt_flag": "{:03}{}".format(line.id, line.chatgpt_username[:3]),
"plan_type": line.plan_type
"plan_type": line.plan_type,
"auth_status": line.auth_status,
})

return Response({"results": results})
Expand Down Expand Up @@ -60,9 +74,19 @@ def post(self, request, *args, **kwargs):

class UserAccountView(generics.ListCreateAPIView):
permission_classes = (IsAuthenticated, IsAdminUser)
queryset = User.objects.order_by("-id").all()
serializer_class = ShowUserAccountModelSerializer
pagination_class = DefaultPageNumberPagination

def get(self, request, *args, **kwargs):
queryset = User.objects.order_by("-id").all()
pg = DefaultPageNumberPagination()
pg.page_size_query_param = "page_size"
page_accounts = pg.paginate_queryset(queryset, request=request)
username_list = [i.username for i in page_accounts]
try:
use_count_dict = req_gateway("post", "/api/get-user-use-count", json={"username_list": username_list})
except:
use_count_dict = {}
serializer = ShowUserAccountModelSerializer(instance=page_accounts, use_count_dict=use_count_dict, many=True)
return pg.get_paginated_response(serializer.data)

def post(self, request, *args, **kwargs):
# 添加或更新用户
Expand Down
24 changes: 14 additions & 10 deletions backend/app/accounts/views/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from app.accounts.serializers import UserRegisterSerializer
from app.chatgpt.models import ChatgptAccount
from app.settings import ADMIN_USERNAME, FREE_ACCOUNT_USERNAME
from app.settings import CHATGPT_GATEWAY_URL
from app.settings import ALLOW_REGISTER
from app.utils import save_visit_log, req_gateway


Expand All @@ -39,7 +39,6 @@ def post(self, request, *args, **kwargs):
user.last_login = timezone.now()
user.save()


token, created = Token.objects.get_or_create(user=user)
request.user = user

Expand All @@ -50,13 +49,17 @@ def post(self, request, *args, **kwargs):
result.update({"is_admin": True})
return Response(result)


class AccountRegister(APIView):
def post(self, request, *args, **kwargs):

if not ALLOW_REGISTER:
raise ValidationError({"message": "当前系统禁止注册账号"})

serializer = UserRegisterSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
url = CHATGPT_GATEWAY_URL + "/api/get-user-info"

res_json = req_gateway("post", url, json={"chatgpt_token": serializer.data["chatgpt_token"]})
res_json = req_gateway("post", "/api/get-user-info", json={"chatgpt_token": serializer.data["chatgpt_token"]})
chatgptaccount_id = ChatgptAccount.save_data(res_json)

user = User.objects.filter(username=serializer.data["username"]).first()
Expand All @@ -65,17 +68,18 @@ def post(self, request, *args, **kwargs):

# 创建默认号池
from app.chatgpt.models import ChatgptCar
chatgptcar, created = ChatgptCar.objects.get_or_create(car_name="reg_{}".format(serializer.data["username"]), defaults={
"created_time":int(time.time()),
"updated_time":int(time.time()),
"remark": "用户注册时,系统自动创建"
})
chatgptcar, created = ChatgptCar.objects.get_or_create(
car_name="reg_{}".format(serializer.data["username"]),
defaults={
"created_time": int(time.time()),
"updated_time": int(time.time()),
"remark": "用户注册时,系统自动创建"
})
gpt_account_list = list(chatgptcar.gpt_account_list)
gpt_account_list.append(chatgptaccount_id)
chatgptcar.gpt_account_list = list(set(gpt_account_list))
chatgptcar.save()


user, created = User.objects.get_or_create(username=serializer.data["username"])
user.set_password(serializer.data["password"])
user.last_login = timezone.now()
Expand Down
16 changes: 16 additions & 0 deletions backend/app/chatgpt/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ class ChatgptAccount(models.Model):
created_time = models.IntegerField(db_index=True, blank=True, verbose_name="创建时间")
updated_time = models.IntegerField(db_index=True, blank=True, verbose_name="最后修改时间")

@classmethod
def get_by_gptcar_list(cls, gptcar_list):
chatgpt_account_list = []
for line in ChatgptCar.objects.filter(id__in=gptcar_list).values("gpt_account_list"):
chatgpt_account_list.extend(line["gpt_account_list"])

gptaccount = ChatgptAccount.objects
if gptcar_list:
gptaccount = gptaccount.filter(id__in=chatgpt_account_list)

return gptaccount.order_by("-id").all()


@classmethod
def get_by_id(cls, chatgpt_id):
return cls.objects.filter(id=chatgpt_id).first()
Expand All @@ -38,9 +51,12 @@ def save_data(cls, data):

if data.get("session_token"):
new_obj.session_token = data["session_token"]
new_obj.refresh_token = None

if data.get("refresh_token"):
new_obj.refresh_token = data["refresh_token"]
new_obj.session_token = None


new_obj.updated_time = int(time.time())

Expand Down
8 changes: 8 additions & 0 deletions backend/app/chatgpt/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ class DeleteChatgptCarSerializer(serializers.Serializer):

class ShowChatgptTokenSerializer(serializers.ModelSerializer):
access_token_exp = serializers.SerializerMethodField()
use_count = serializers.SerializerMethodField()

def __init__(self, *args, use_count_dict=dict, **kwargs):
super().__init__(*args, **kwargs)
self.use_count_dict = use_count_dict

def get_use_count(self, obj):
return self.use_count_dict.get(obj.chatgpt_username, 0)

def get_access_token_exp(self, obj):
try:
Expand Down
Loading

0 comments on commit e39f220

Please sign in to comment.