Skip to content

Commit 5180f7c

Browse files
authored
Merge branch 'main' into pr@main@feat_log-management
2 parents a2aa723 + 9428875 commit 5180f7c

File tree

35 files changed

+769
-61
lines changed

35 files changed

+769
-61
lines changed

.github/workflows/build_and_push.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
inputs:
88
dockerImageTag:
99
description: 'Image Tag'
10-
default: 'v0.9.0'
10+
default: 'dev'
1111
required: true
1212
dockerImageTagWithLatest:
1313
description: '是否发布latest tag(正式发版时选择,测试版本切勿选择)'

.github/workflows/xpack.yml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# .github/workflows/wheel.yml
2+
name: Build Wheels Universal
3+
4+
on:
5+
workflow_dispatch:
6+
inputs:
7+
8+
target_os:
9+
description: 'Select OS to run on'
10+
required: false
11+
default: '["ubuntu-latest","macos-latest","windows-latest","macos-15-intel","ubuntu-24.04-arm"]'
12+
type: choice
13+
options:
14+
- '["macos-latest"]'
15+
- '["macos-15-intel"]'
16+
- '["ubuntu-latest"]'
17+
- '["windows-latest"]'
18+
- '["ubuntu-24.04-arm"]'
19+
- '["ubuntu-latest","windows-latest"]'
20+
- '["ubuntu-latest","macos-latest"]'
21+
- '["macos-latest","windows-latest"]'
22+
- '["ubuntu-latest","macos-latest","windows-latest"]'
23+
- '["ubuntu-latest","macos-latest","windows-latest","macos-15-intel","ubuntu-24.04-arm"]'
24+
publish:
25+
description: 'Publish to repository'
26+
required: false
27+
default: false
28+
type: boolean
29+
repository:
30+
description: 'Select repository to publish (Attention: Disable pypi selection until release)'
31+
required: false
32+
default: 'testpypi'
33+
type: choice
34+
options:
35+
- testpypi
36+
- pypi
37+
xpack_branch:
38+
description: 'SQLBot X-Pack Branch'
39+
required: true
40+
type: choice
41+
default: "main"
42+
options:
43+
- main
44+
45+
jobs:
46+
build:
47+
runs-on: ${{ matrix.os }}
48+
environment: sqlbot-xpack-env
49+
defaults:
50+
run:
51+
working-directory: ./sqlbot-xpack
52+
strategy:
53+
matrix:
54+
os: ${{ fromJSON(inputs.target_os) }}
55+
steps:
56+
- uses: actions/checkout@v4
57+
- name: Check out SQLBot xpack repo
58+
uses: actions/checkout@v4
59+
with:
60+
repository: dataease/sqlbot-xpack
61+
ref: ${{ inputs.xpack_branch }}
62+
path: ./sqlbot-xpack
63+
token: ${{ secrets.GH_TOKEN }}
64+
- name: Use Node.js ${{ matrix.node-version }}
65+
uses: actions/setup-node@v4
66+
with:
67+
node-version: ${{ matrix.node-version }}
68+
- name: Install and build
69+
working-directory: ./sqlbot-xpack/xpack_static
70+
run: |
71+
npm install
72+
npm run build:prod
73+
- name: Set up Python
74+
uses: actions/setup-python@v5
75+
with:
76+
python-version: '3.11'
77+
- name: Install build tools
78+
run: |
79+
python -m pip install --upgrade pip
80+
pip install cython wheel twine
81+
- name: Install the latest version of uv
82+
uses: astral-sh/setup-uv@v6
83+
with:
84+
version: "latest"
85+
- name: Install dependencies
86+
run: uv sync
87+
- name: Build encrypted
88+
run: python build_scripts/build.py
89+
- name: Pre build wheel
90+
run: python ./build_scripts/format.py encrypted
91+
- name: Build wheel
92+
run: python -m pip wheel --no-deps -w dist .
93+
- name: Finish build wheek
94+
run: python ./build_scripts/format.py
95+
- name: Show wheel
96+
run: unzip -l dist/*
97+
- name: Publish to repository
98+
if: ${{ inputs.publish }}
99+
run: twine upload --repository-url ${{ inputs.repository == 'pypi' && 'https://upload.pypi.org/legacy/' || 'https://test.pypi.org/legacy/' }} -u __token__ -p ${{ inputs.repository == 'pypi' && secrets.FORMAL_PYPI_API_TOKEN || secrets.PYPI_API_TOKEN }} dist/*.whl --verbose
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""056_api_key_ddl
2+
3+
Revision ID: d9a5589fc00b
4+
Revises: 3d4bd2d673dc
5+
Create Date: 2025-12-23 13:41:26.705947
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
from sqlalchemy.dialects import postgresql
12+
13+
# revision identifiers, used by Alembic.
14+
revision = 'd9a5589fc00b'
15+
down_revision = '3d4bd2d673dc'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.create_table('sys_apikey',
23+
sa.Column('access_key', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False),
24+
sa.Column('secret_key', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False),
25+
sa.Column('create_time', sa.BigInteger(), nullable=False),
26+
sa.Column('uid', sa.BigInteger(), nullable=False),
27+
sa.Column('status', sa.Boolean(), nullable=False),
28+
sa.Column('id', sa.BigInteger(), nullable=False),
29+
sa.PrimaryKeyConstraint('id')
30+
)
31+
op.create_index(op.f('ix_sys_apikey_id'), 'sys_apikey', ['id'], unique=False)
32+
# ### end Alembic commands ###
33+
34+
35+
def downgrade():
36+
# ### commands auto generated by Alembic - please adjust! ###
37+
op.drop_index(op.f('ix_sys_apikey_id'), table_name='sys_apikey')
38+
op.drop_table('sys_apikey')
39+
# ### end Alembic commands ###

backend/apps/api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from apps.data_training.api import data_training
66
from apps.datasource.api import datasource, table_relation, recommended_problem
77
from apps.mcp import mcp
8-
from apps.system.api import login, user, aimodel, workspace, assistant, parameter
8+
from apps.system.api import login, user, aimodel, workspace, assistant, parameter, apikey
99
from apps.terminology.api import terminology
1010
from apps.settings.api import base
1111

@@ -25,5 +25,6 @@
2525
api_router.include_router(mcp.router)
2626
api_router.include_router(table_relation.router)
2727
api_router.include_router(parameter.router)
28+
api_router.include_router(apikey.router)
2829

2930
api_router.include_router(recommended_problem.router)

backend/apps/chat/api/chat.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def _err(_e: Exception):
159159

160160
@router.get("/recent_questions/{datasource_id}", response_model=List[str],
161161
summary=f"{PLACEHOLDER_PREFIX}get_recommend_questions")
162-
@require_permissions(permission=SqlbotPermission(type='ds', keyExpression="datasource_id"))
162+
#@require_permissions(permission=SqlbotPermission(type='ds', keyExpression="datasource_id"))
163163
async def recommend_questions(session: SessionDep, current_user: CurrentUser,
164164
datasource_id: int = Path(..., description=f"{PLACEHOLDER_PREFIX}ds_id")):
165165
return list_recent_questions(session=session, current_user=current_user, datasource_id=datasource_id)
@@ -201,7 +201,7 @@ async def question_answer_inner(session: SessionDep, current_user: CurrentUser,
201201
stmt = select(ChatRecord.id, ChatRecord.chat_id, ChatRecord.analysis_record_id,
202202
ChatRecord.predict_record_id, ChatRecord.regenerate_record_id,
203203
ChatRecord.first_chat).where(
204-
and_(ChatRecord.id == record_id))
204+
and_(ChatRecord.id == record_id)).order_by(ChatRecord.create_time.desc())
205205
_record = session.execute(stmt).fetchone()
206206
if not _record:
207207
raise Exception(f'Record id: {record_id} does not exist')

backend/apps/datasource/models/datasource.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ def __init__(self, attr1, attr2):
148148

149149

150150
class TableSchemaResponse(BaseModel):
151-
tableName: str = None
152-
tableComment: str = None
151+
tableName: str = ''
152+
tableComment: str | None = ''
153153

154154

155155
class ColumnSchema:
@@ -164,9 +164,9 @@ def __init__(self, attr1, attr2, attr3):
164164

165165

166166
class ColumnSchemaResponse(BaseModel):
167-
fieldName: str
168-
fieldType: str
169-
fieldComment: str
167+
fieldName: str | None = ''
168+
fieldType: str | None = ''
169+
fieldComment: str | None = ''
170170

171171

172172
class TableAndFields:
@@ -183,7 +183,8 @@ def __init__(self, schema, table, fields):
183183
class FieldObj(BaseModel):
184184
fieldName: str | None
185185

186+
186187
class PreviewResponse(BaseModel):
187-
fields:List = []
188-
data:List = []
189-
sql:str = ''
188+
fields: List | None = []
189+
data: List | None = []
190+
sql: str | None = ''

backend/apps/db/db_sql.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def get_table_sql(ds: CoreDatasource, conf: DatasourceConf, db_version: str = ''
148148
elif equals_ignore_case(ds.type, "kingbase"):
149149
return """
150150
SELECT c.relname AS TABLE_NAME,
151-
COALESCE(d.description, obj_description(c.oid)) AS TABLE_COMMENT
151+
COALESCE(COALESCE(d.description, obj_description(c.oid)), '') AS TABLE_COMMENT
152152
FROM pg_class c
153153
LEFT JOIN
154154
pg_namespace n ON n.oid = c.relnamespace

backend/apps/system/api/apikey.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
from fastapi import APIRouter
3+
from sqlmodel import func, select
4+
from apps.system.crud.apikey_manage import clear_api_key_cache
5+
from apps.system.models.system_model import ApiKeyModel
6+
from apps.system.schemas.system_schema import ApikeyGridItem, ApikeyStatus
7+
from common.core.deps import CurrentUser, SessionDep
8+
from common.utils.time import get_timestamp
9+
import secrets
10+
11+
router = APIRouter(tags=["system_apikey"], prefix="/system/apikey", include_in_schema=False)
12+
13+
@router.get("")
14+
async def grid(session: SessionDep, current_user: CurrentUser) -> list[ApikeyGridItem]:
15+
query = select(ApiKeyModel).where(ApiKeyModel.uid == current_user.id).order_by(ApiKeyModel.create_time.desc())
16+
return session.exec(query).all()
17+
18+
@router.post("")
19+
async def create(session: SessionDep, current_user: CurrentUser):
20+
count = session.exec(select(func.count()).select_from(ApiKeyModel)).one()
21+
if count >= 5:
22+
raise ValueError("Maximum of 5 API keys allowed")
23+
access_key = secrets.token_urlsafe(16)
24+
secret_key = secrets.token_urlsafe(32)
25+
api_key = ApiKeyModel(
26+
access_key=access_key,
27+
secret_key=secret_key,
28+
create_time=get_timestamp(),
29+
uid=current_user.id,
30+
status=True
31+
)
32+
session.add(api_key)
33+
session.commit()
34+
return api_key.id
35+
36+
@router.put("/status")
37+
async def status(session: SessionDep, current_user: CurrentUser, dto: ApikeyStatus):
38+
api_key = session.get(ApiKeyModel, dto.id)
39+
if not api_key:
40+
raise ValueError("API Key not found")
41+
if api_key.uid != current_user.id:
42+
raise PermissionError("No permission to modify this API Key")
43+
if dto.status == api_key.status:
44+
return
45+
api_key.status = dto.status
46+
await clear_api_key_cache(api_key.access_key)
47+
session.add(api_key)
48+
session.commit()
49+
50+
@router.delete("/{id}")
51+
async def delete(session: SessionDep, current_user: CurrentUser, id: int):
52+
api_key = session.get(ApiKeyModel, id)
53+
if not api_key:
54+
raise ValueError("API Key not found")
55+
if api_key.uid != current_user.id:
56+
raise PermissionError("No permission to delete this API Key")
57+
await clear_api_key_cache(api_key.access_key)
58+
session.delete(api_key)
59+
session.commit()
60+

backend/apps/system/api/assistant.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ async def get_one(session: SessionDep, id: int = Path(description="ID")):
195195
return db_model
196196

197197

198-
@router.delete("/{id}", response_model=AssistantModel, summary=f"{PLACEHOLDER_PREFIX}assistant_del_api", description=f"{PLACEHOLDER_PREFIX}assistant_del_api")
198+
@router.delete("/{id}", summary=f"{PLACEHOLDER_PREFIX}assistant_del_api", description=f"{PLACEHOLDER_PREFIX}assistant_del_api")
199199
@clear_cache(namespace=CacheNamespace.EMBEDDED_INFO, cacheName=CacheName.ASSISTANT_INFO, keyExpression="id")
200200
async def delete(request: Request, session: SessionDep, id: int = Path(description="ID")):
201201
db_model = session.get(AssistantModel, id)

backend/apps/system/api/user.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ async def ws_change(session: SessionDep, current_user: CurrentUser, trans:Trans,
127127
user_model: UserModel = get_db_user(session = session, user_id = current_user.id)
128128
user_model.oid = oid
129129
session.add(user_model)
130-
session.commit()
131130

132131
@router.get("/{id}", response_model=UserEditor, summary=f"{PLACEHOLDER_PREFIX}user_detail_api", description=f"{PLACEHOLDER_PREFIX}user_detail_api")
133132
@require_permissions(permission=SqlbotPermission(role=['admin']))
@@ -142,6 +141,9 @@ async def query(session: SessionDep, trans: Trans, id: int = Path(description=f"
142141

143142
@router.post("", summary=f"{PLACEHOLDER_PREFIX}user_create_api", description=f"{PLACEHOLDER_PREFIX}user_create_api")
144143
@require_permissions(permission=SqlbotPermission(role=['admin']))
144+
async def user_create(session: SessionDep, creator: UserCreator, trans: Trans):
145+
await create(session=session, creator=creator, trans=trans)
146+
145147
async def create(session: SessionDep, creator: UserCreator, trans: Trans):
146148
if check_account_exists(session=session, account=creator.account):
147149
raise Exception(trans('i18n_exist', msg = f"{trans('i18n_user.account')} [{creator.account}]"))
@@ -167,7 +169,6 @@ async def create(session: SessionDep, creator: UserCreator, trans: Trans):
167169
session.add_all(db_model_list)
168170
user_model.oid = creator.oid_list[0]
169171
session.add(user_model)
170-
session.commit()
171172

172173

173174
@router.put("", summary=f"{PLACEHOLDER_PREFIX}user_update_api", description=f"{PLACEHOLDER_PREFIX}user_update_api")
@@ -204,7 +205,6 @@ async def update(session: SessionDep, editor: UserEditor, trans: Trans):
204205
session.add_all(db_model_list)
205206
user_model.oid = origin_oid if origin_oid in editor.oid_list else editor.oid_list[0]
206207
session.add(user_model)
207-
session.commit()
208208

209209
@router.delete("/{id}", summary=f"{PLACEHOLDER_PREFIX}user_del_api", description=f"{PLACEHOLDER_PREFIX}user_del_api")
210210
@require_permissions(permission=SqlbotPermission(role=['admin']))
@@ -226,7 +226,6 @@ async def langChange(session: SessionDep, current_user: CurrentUser, trans: Tran
226226
db_user: UserModel = get_db_user(session=session, user_id=current_user.id)
227227
db_user.language = lang
228228
session.add(db_user)
229-
session.commit()
230229

231230

232231
@router.patch("/pwd/{id}", summary=f"{PLACEHOLDER_PREFIX}reset_pwd", description=f"{PLACEHOLDER_PREFIX}reset_pwd")
@@ -238,7 +237,6 @@ async def pwdReset(session: SessionDep, current_user: CurrentUser, trans: Trans,
238237
db_user: UserModel = get_db_user(session=session, user_id=id)
239238
db_user.password = default_md5_pwd()
240239
session.add(db_user)
241-
session.commit()
242240

243241
@router.put("/pwd", summary=f"{PLACEHOLDER_PREFIX}update_pwd", description=f"{PLACEHOLDER_PREFIX}update_pwd")
244242
@clear_cache(namespace=CacheNamespace.AUTH_INFO, cacheName=CacheName.USER_INFO, keyExpression="current_user.id")
@@ -251,7 +249,6 @@ async def pwdUpdate(session: SessionDep, current_user: CurrentUser, trans: Trans
251249
raise Exception(trans('i18n_error', key = trans('i18n_user.password')))
252250
db_user.password = md5pwd(new_pwd)
253251
session.add(db_user)
254-
session.commit()
255252

256253

257254
@router.patch("/status", summary=f"{PLACEHOLDER_PREFIX}update_status", description=f"{PLACEHOLDER_PREFIX}update_status")
@@ -265,5 +262,4 @@ async def statusChange(session: SessionDep, current_user: CurrentUser, trans: Tr
265262
return {"message": "status not supported"}
266263
db_user: UserModel = get_db_user(session=session, user_id=statusDto.id)
267264
db_user.status = status
268-
session.add(db_user)
269-
session.commit()
265+
session.add(db_user)

0 commit comments

Comments
 (0)