tanbushi commited on
Commit
daa9d8a
·
1 Parent(s): ec3714b
Files changed (12) hide show
  1. .gitignore +2 -1
  2. api_keys.db +0 -0
  3. app.py +47 -12
  4. captcha.png +0 -0
  5. db.py +64 -0
  6. requirements.txt +5 -1
  7. routers/openai_v1.py +86 -0
  8. routers/users_v1.py +48 -0
  9. routers/webtools_v1.py +29 -0
  10. sql.md +88 -0
  11. test_db.py +22 -0
  12. users.sql +41 -0
.gitignore CHANGED
@@ -1 +1,2 @@
1
- __pycache__/
 
 
1
+ __pycache__/
2
+ .env
api_keys.db ADDED
Binary file (12.3 kB). View file
 
app.py CHANGED
@@ -1,19 +1,54 @@
1
  # uvicorn app:app --host 0.0.0.0 --port 7860 --reload
2
 
3
- from fastapi import FastAPI, HTTPException
4
  from starlette.requests import Request
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  app = FastAPI()
7
 
 
 
 
 
8
  @app.get("/")
9
- def greet_json():
10
- return {"Hello": "World!"}
11
-
12
- @app.post("/airs/v1/chat/completions")
13
- async def chat_completions(request:Request):
14
- auth_header = request.headers['authorization']
15
- api_key = auth_header.split()[1] # 分割字符串并取第二个元素
16
- if api_key != "iam-tanbushi":
17
- raise HTTPException(status_code=401, detail="Invalid API Key")
18
- data = await request.json()
19
- return {"data": data}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # uvicorn app:app --host 0.0.0.0 --port 7860 --reload
2
 
3
+ from fastapi import FastAPI, HTTPException, Response, Depends
4
  from starlette.requests import Request
5
 
6
+ from routers.webtools_v1 import router as webtools_router
7
+ from routers.users_v1 import router as users_router
8
+ from routers.openai_v1 import router as openai_router
9
+
10
+ from db import Db
11
+
12
+ from dotenv import load_dotenv
13
+ import os
14
+
15
+ # 加载.env文件
16
+ load_dotenv()
17
+
18
+ DB_PATH = os.getenv("DB_PATH")
19
+
20
+ # 依赖注入,获取 DB_PATH
21
+ def get_db_path():
22
+ return DB_PATH
23
+
24
  app = FastAPI()
25
 
26
+ app.include_router(webtools_router, prefix="/airs/v1", tags=["webtools"])
27
+ app.include_router(users_router, prefix="/airs/v1", tags=["users"])
28
+ app.include_router(openai_router, prefix="/airs/v1", tags=["openai"])
29
+
30
  @app.get("/")
31
+ def greet_json(db_path: str = Depends(get_db_path)):
32
+ print(DB_PATH)
33
+ return {"Hello": "World!"}
34
+ @app.get("/init_db")
35
+ def init_db():
36
+ sql = """
37
+ CREATE TABLE api_keys (
38
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
39
+ api_key TEXT NOT NULL,
40
+ type TEXT NOT NULL,
41
+ status INTEGER NOT NULL,
42
+ idx INTEGER NOT NULL,
43
+ dest_api_key TEXT NOT NULL
44
+ );
45
+ """
46
+ Db('api_keys.db').execute_query(sql)
47
+
48
+ # def create_user_api_key(user_id, api_key, type, status, idx, dest_api_key):
49
+ # sql = f"""
50
+ # INSERT INTO api_keys (api_key, type, status, idx, dest_api_key)
51
+ # """
52
+ # Db('api_keys.db').execute_query(sql)
53
+
54
+ # create_user_api_key('i_am_tanbushi', 'llm', '1', '0', 'dest_api_key')
captcha.png ADDED
db.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+ import json
3
+
4
+ class Db():
5
+ def __init__(self, db_path):
6
+ self.db_path = db_path
7
+ # print(self.db_path)
8
+
9
+ def list_query(self, query):
10
+ conn = sqlite3.connect(self.db_path)
11
+ cursor = conn.cursor()
12
+ cursor.execute(query)
13
+ conn.commit()
14
+
15
+ rows = cursor.fetchall()
16
+ # print('results')
17
+ # print(results)
18
+ cursor.close()
19
+ conn.close()
20
+
21
+ json_rows = []
22
+ # 为每一行结果创建一个字典,并添加到列表中
23
+ for row in rows:
24
+ # 使用cursor.description获取列名和相关信息
25
+ columns = dict(zip([col[0] for col in cursor.description], row))
26
+ json_rows.append(columns)
27
+
28
+ return json_rows
29
+
30
+ def insert_query(self, query):
31
+ conn = sqlite3.connect(self.db_path)
32
+ cursor = conn.cursor()
33
+ cursor.execute(query)
34
+ conn.commit()
35
+ rowcount = cursor.rowcount
36
+
37
+ return f"插入的行数: {rowcount}"
38
+
39
+ def update_query(self, query):
40
+ conn = sqlite3.connect(self.db_path)
41
+ cursor = conn.cursor()
42
+ cursor.execute(query)
43
+ conn.commit()
44
+ rowcount = cursor.rowcount
45
+
46
+ return f"影响的行数: {rowcount}"
47
+ # from fastapi import FastAPI
48
+
49
+ # # 依赖注入,获取 FastAPI 应用实例
50
+ # def get_app() -> FastAPI:
51
+ # return FastAPI()
52
+
53
+ # def execute_query(query):
54
+
55
+
56
+
57
+
58
+ # CREATE TABLE api_keys (
59
+ # api_key TEXT NOT NULL,
60
+ # type TEXT NOT NULL,
61
+ # status INTEGER NOT NULL,
62
+ # idx INTEGER NOT NULL,
63
+ # PRIMARY KEY (api_key, type, status, idx)
64
+ # );
requirements.txt CHANGED
@@ -1,2 +1,6 @@
1
  fastapi
2
- uvicorn[standard]
 
 
 
 
 
1
  fastapi
2
+ uvicorn[standard]
3
+ captcha
4
+ python-dotenv
5
+ langchain_google_genai==1.0.10
6
+ langchain-openai==0.1.25 # 使用 openai 或兼容 openai
routers/openai_v1.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Request, HTTPException
2
+ from db import Db
3
+ from dotenv import load_dotenv
4
+ import os, json
5
+ from langchain_core.output_parsers import StrOutputParser
6
+ from langchain_core.prompts import ChatPromptTemplate
7
+
8
+ # 加载.env文件
9
+ load_dotenv()
10
+
11
+ router = APIRouter()
12
+
13
+ @router.post("/chat/completions")
14
+ async def chat_completions(request:Request):
15
+ print('chat_completions')
16
+ auth_header = request.headers['authorization']
17
+ user_api_key = auth_header.split()[1] # 分割字符串并取第二个元素
18
+ data = await request.json()
19
+ try:
20
+ model = data['model']
21
+ except:
22
+ model = ''
23
+ if model=='':
24
+ model = await get_default_model()
25
+ api_key_info = await get_api_key(model)
26
+ api_key = api_key_info['api_key']
27
+ group_name = api_key_info['group_name']
28
+
29
+
30
+ # 下面是google api
31
+ if group_name=='gemini':
32
+ from langchain_google_genai import ChatGoogleGenerativeAI
33
+ # 初始化模型
34
+ llm = ChatGoogleGenerativeAI(
35
+ api_key = api_key,
36
+ model = model,
37
+ )
38
+
39
+
40
+ # 下面就是 chatgpt 兼容 api
41
+ if group_name=='glm':
42
+ from langchain_openai import ChatOpenAI
43
+ # 初始化 ChatOpenAI 模型
44
+ llm = ChatOpenAI(
45
+ model = model,
46
+ api_key = api_key,
47
+ base_url = 'https://open.bigmodel.cn/api/paas/v4', # 下一步将base_url 写到数据库里
48
+ )
49
+
50
+ messages = data['messages']
51
+ rslt = [(item["role"], item["content"]) for item in messages] # 转换为所需的元组列表格式
52
+ parser = StrOutputParser()
53
+ prompt_template = ChatPromptTemplate.from_messages(rslt)
54
+
55
+ chain = prompt_template | llm | parser
56
+ result = chain.invoke({})
57
+ return result
58
+
59
+ async def get_default_model():
60
+ results = Db(os.getenv("DB_PATH")).list_query("SELECT * FROM api_names order by default_order limit 1")
61
+ try:
62
+ result = results[0]['api_name']
63
+ except:
64
+ result = ''
65
+ return result
66
+
67
+ async def get_api_key(model):
68
+ sql = f"""
69
+ SELECT an.api_name, ak.api_key, ag.group_name
70
+ FROM api_keys ak
71
+ JOIN api_groups ag ON ak.api_group_id = ag.id
72
+ JOIN api_names an ON an.api_group_id = ag.id
73
+ WHERE ak.category='LLM' and an.api_name='{model}' and disabled=0
74
+ ORDER BY ak.last_call_at
75
+ limit 1
76
+ """
77
+ results = Db(os.getenv("DB_PATH")).list_query(sql)
78
+ try:
79
+ result = results[0]
80
+ api_key = result['api_key']
81
+ except:
82
+ api_key = ''
83
+
84
+ sql = f"update api_keys set last_call_at=datetime('now') where api_key='{api_key}'"
85
+ Db(os.getenv("DB_PATH")).update_query(sql)
86
+ return result
routers/users_v1.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Request, FastAPI, Depends
2
+ from dotenv import load_dotenv
3
+ import os
4
+ import uuid
5
+ from db import Db
6
+
7
+ router = APIRouter()
8
+
9
+ # 加载.env文件
10
+ load_dotenv()
11
+
12
+ DB_PATH = os.getenv("DB_PATH")
13
+
14
+ # 依赖注入,获取 DB_PATH
15
+ def get_db_path():
16
+ return DB_PATH
17
+
18
+ @router.get("/users")
19
+ async def read_users(db_path: str = Depends(get_db_path)):
20
+ # print('\n\n\n\n\n')
21
+ # print(db_path)
22
+ result = Db(db_path).list_query("SELECT * FROM users")
23
+ return result
24
+
25
+ @router.post("/users")
26
+ async def create_user(request: Request, b_path: str = Depends(get_db_path)):
27
+ request_json = await request.json()
28
+ username = request_json.get('username')
29
+ password = request_json.get('password')
30
+ email = request_json.get('email')
31
+ nikename = request_json.get('nikename')
32
+ if nikename==None:
33
+ nikename = ''
34
+ # print(nikename)
35
+ api_key = f'airs-{uuid.uuid4()}'
36
+ # result = api_key
37
+ # print(api_key)
38
+ result = Db(b_path).insert_query(f"INSERT INTO users (username, password, email, nikename, api_key) VALUES ('{username}', '{password}', '{email}', '{nikename}', '{api_key}')")
39
+ return result
40
+
41
+ @router.get("/users/{id}")
42
+ async def read_user(request: Request, id:int, db_path: str = Depends(get_db_path)):
43
+ result = Db(db_path).list_query(f"SELECT * FROM users where id={id}")
44
+ if len(result)>0:
45
+ return result[0]
46
+ else:
47
+ return None
48
+
routers/webtools_v1.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, APIRouter, Response, Depends
2
+ from captcha.image import ImageCaptcha
3
+ from io import BytesIO
4
+
5
+ router = APIRouter()
6
+
7
+ # 依赖注入,获取 FastAPI 应用实例
8
+ def get_app() -> FastAPI:
9
+ return FastAPI()
10
+
11
+ @router.get("/webtools/captcha")
12
+ async def get_captcha(app: FastAPI = Depends(get_app)):
13
+ # 生成随机验证码字符串
14
+ captcha_text = "123456" # 这里只是一个示例,实际应用中应该生成一个随机字符串
15
+ # 创建验证码图片
16
+ image = ImageCaptcha(width=280, height=90)
17
+ image.write(captcha_text, "captcha.png")
18
+
19
+ # 将验证码字符串存储在内存中,以便后续验证
20
+ # 注意:实际应用中应该使用更安全的方式存储验证码,例如使用会话或数据库
21
+ app.captcha_text = captcha_text
22
+
23
+ # 读取图片内容并返回
24
+ image_buffer = BytesIO()
25
+ image.write(captcha_text, image_buffer)
26
+ image_buffer.seek(0)
27
+
28
+ # 返回图片数据
29
+ return Response(content=image_buffer.getvalue(), media_type="image/png")
sql.md ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 当然,以下是完整的SQL语句,用于创建`api_names`、`api_keys`表,以及可选的`api_usage`表。这些表将支持存储API名称、API-KEY信息,并可选地记录API的使用情况。
2
+
3
+ ### SQL 创建表的语句
4
+
5
+ ```sql
6
+ -- 创建 users 表
7
+ DROP TABLE IF EXISTS "users";
8
+ CREATE TABLE "users" (
9
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ "username" TEXT NOT NULL,
11
+ "password" TEXT NOT NULL,
12
+ "email" TEXT NOT NULL,
13
+ "nikename" TEXT,
14
+ "created_at" DATETIME DEFAULT CURRENT_TIMESTAMP,
15
+ "updated_at" DATETIME DEFAULT CURRENT_TIMESTAMP,
16
+ UNIQUE ("username" ASC),
17
+ UNIQUE ("email" ASC)
18
+ );
19
+
20
+ -- 创建api_groups表
21
+ CREATE TABLE api_groups (
22
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
23
+ group_name TEXT NOT NULL UNIQUE,
24
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
25
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
26
+ );
27
+
28
+ -- 创建api_names表
29
+ CREATE TABLE api_names (
30
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
31
+ api_name TEXT NOT NULL UNIQUE,
32
+ api_group_id INTEGER,
33
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
34
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
35
+ FOREIGN KEY (api_group_id) REFERENCES api_groups(id) ON DELETE SET NULL
36
+ );
37
+
38
+ -- 创建api_keys表
39
+ CREATE TABLE api_keys (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ api_group_id INTEGER NOT NULL,
42
+ api_key TEXT NOT NULL,
43
+ category TEXT NOT NULL,
44
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
45
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
46
+ FOREIGN KEY (api_group_id) REFERENCES api_groups(id) ON DELETE CASCADE
47
+ );
48
+
49
+ -- 创建user_api_keys表
50
+ CREATE TABLE user_api_keys (
51
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
52
+ user_api_key TEXT NOT NULL,
53
+ user_id INTEGER,
54
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
55
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
56
+ );
57
+
58
+ -- 创建api_usage表(如果需要)
59
+ CREATE TABLE api_usage (
60
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
61
+ user_api_key_id INTEGER,
62
+ api_key_id INTEGER,
63
+ request_count INTEGER DEFAULT 0,
64
+ last_used DATETIME DEFAULT CURRENT_TIMESTAMP,
65
+ FOREIGN KEY (user_api_key_id) REFERENCES user_api_keys(id),
66
+ FOREIGN KEY (api_key_id) REFERENCES api_keys(id)
67
+ );
68
+ ```
69
+
70
+ ### 说明
71
+
72
+ - **`api_names` 表**:存储唯一的API名称。
73
+ - **`api_keys` 表**:存储每个API名称对应的多个API-KEY,包括分类信息。`api_names_id`作为外键,关联到`api_names`表。
74
+ - **`user_api_keys` 表**:存储用户的API-KEY。
75
+ - **`api_usage` 表**(可选):记录API的使用情况,包括请求次数和最后使用时间。这个表可以用来监控API的使用情况,帮助管理API-KEY的使用限额。
76
+
77
+ 这些SQL语句假设你使用的是MySQL数据库。如果你使用的是其他类型的数据库(如PostgreSQL、SQLite等),可能需要对SQL语句进行适当的调整。如果有任何问题或需要进一步的帮助,请随时告诉我。
78
+
79
+
80
+
81
+ ### 通过 api_name 查询 api_key 的 SQL 语句
82
+ ```sql
83
+ SELECT ak.api_key
84
+ FROM api_keys ak
85
+ JOIN api_groups ag ON ak.api_group_id = ag.id
86
+ JOIN api_names an ON an.api_group_id = ag.id
87
+ WHERE an.api_name = 'your_api_name';
88
+ ```
test_db.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PC上数据库的位置为:/mnt/f/微云同步目录/1913864316/dev/projects/api-mapper
2
+
3
+ from db import Db
4
+ from dotenv import load_dotenv
5
+ import os
6
+
7
+ # 加载.env文件
8
+ load_dotenv()
9
+
10
+ db = Db(os.getenv("DB_PATH"))
11
+
12
+ results = db.execute_query("""
13
+ SELECT an.api_name, ak.api_key
14
+ FROM api_keys ak
15
+ JOIN api_groups ag ON ak.api_group_id = ag.id
16
+ JOIN api_names an ON an.api_group_id = ag.id
17
+ WHERE an.api_name = 'glm-4-flash';
18
+ """)
19
+
20
+
21
+ for row in results:
22
+ print(row)
users.sql ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Navicat Premium Dump SQL
3
+
4
+ Source Server : api_keys
5
+ Source Server Type : SQLite
6
+ Source Server Version : 3045000 (3.45.0)
7
+ Source Schema : main
8
+
9
+ Target Server Type : SQLite
10
+ Target Server Version : 3045000 (3.45.0)
11
+ File Encoding : 65001
12
+
13
+ Date: 03/11/2024 20:45:36
14
+ */
15
+
16
+ PRAGMA foreign_keys = false;
17
+
18
+ -- ----------------------------
19
+ -- Table structure for users
20
+ -- ----------------------------
21
+ DROP TABLE IF EXISTS "users";
22
+ CREATE TABLE "users" (
23
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT,
24
+ "username" TEXT NOT NULL,
25
+ "password" TEXT NOT NULL,
26
+ "email" TEXT NOT NULL,
27
+ "nikename" TEXT,
28
+ "api_key" TEXT,
29
+ "config" TEXT,
30
+ "created_at" DATETIME DEFAULT CURRENT_TIMESTAMP,
31
+ "updated_at" DATETIME DEFAULT CURRENT_TIMESTAMP,
32
+ UNIQUE ("username" ASC),
33
+ UNIQUE ("email" ASC)
34
+ );
35
+
36
+ -- ----------------------------
37
+ -- Auto increment value for users
38
+ -- ----------------------------
39
+ UPDATE "sqlite_sequence" SET seq = 5 WHERE name = 'users';
40
+
41
+ PRAGMA foreign_keys = true;