Merge branch 'main' into fix/privacy-persistence-and-ui-consistency

This commit is contained in:
4pmtong 2026-03-05 23:56:00 +08:00
commit 09aef22920
5 changed files with 121 additions and 18 deletions

View file

@ -77,7 +77,7 @@ def get_privacy(session: Session = Depends(session), auth: Auth = Depends(auth_m
return UserPrivacySettings.default_settings()
logger.debug("Privacy settings retrieved", extra={"user_id": user_id})
return model.pricacy_setting
return UserPrivacySettings(**model.pricacy_setting).to_response()
@router.put("/user/privacy", name="update user privacy")
@ -97,7 +97,7 @@ def put_privacy(data: UserPrivacySettings, session: Session = Depends(session),
model.save(session)
logger.info("Privacy settings created", extra={"user_id": user_id})
return model.pricacy_setting
return UserPrivacySettings(**model.pricacy_setting).to_response()
@router.get("/user/current_credits", name="get user current credits")

View file

@ -1,16 +1,18 @@
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
from typing import ClassVar
from pydantic import BaseModel
from sqlalchemy import JSON
@ -31,6 +33,21 @@ class UserPrivacySettings(BaseModel):
access_your_address: bool | None = False
password_storage: bool | None = False
# Fields that must all be True for the user to proceed
REQUIRED_FIELDS: ClassVar[list[str]] = [
'take_screenshot',
'access_local_software',
'access_your_address',
'password_storage',
]
@classmethod
def default_settings(cls) -> dict:
return cls().model_dump()
instance = cls()
return {**instance.model_dump(), "all_required_granted": instance.all_required_granted()}
def all_required_granted(self) -> bool:
return all(getattr(self, f) for f in self.REQUIRED_FIELDS)
def to_response(self) -> dict:
return {**self.model_dump(), "all_required_granted": self.all_required_granted()}

80
server/tests/user.http Normal file
View file

@ -0,0 +1,80 @@
### ============================================
### Eigent Server API Tests
### ============================================
### VSCode Extension: "REST Client" by Huachao Mao
### Extension ID: humao.rest-client
### ============================================
@baseUrl = http://localhost:3001/api
@token = YOUR_TOKEN_HERE
### ──────────────────────────────────────────
### Auth (no token required)
### ──────────────────────────────────────────
### Register a new user
POST {{baseUrl}}/register
Content-Type: application/json
{
"email": "test@example.com",
"password": "testpassword123"
}
### Login
# @name login
POST {{baseUrl}}/login
Content-Type: application/json
{
"email": "test@example.com",
"password": "testpassword123"
}
### Save token from login response (use this after running login)
@authToken = {{login.response.body.token}}
### ──────────────────────────────────────────
### User
### ──────────────────────────────────────────
### Get current user info
GET {{baseUrl}}/user
Authorization: Bearer {{authToken}}
### ──────────────────────────────────────────
### Privacy Settings
### ──────────────────────────────────────────
### Get privacy settings
GET {{baseUrl}}/user/privacy
Authorization: Bearer {{authToken}}
### Update privacy settings
PUT {{baseUrl}}/user/privacy
Authorization: Bearer {{authToken}}
Content-Type: application/json
{
"take_screenshot": true,
"access_local_software": false,
"access_your_address": false,
"password_storage": false
}
### Update only help_improve
PUT {{baseUrl}}/user/privacy
Authorization: Bearer {{authToken}}
Content-Type: application/json
{
"help_improve": true
}
### ──────────────────────────────────────────
### User Stats
### ──────────────────────────────────────────
### Get user stats
GET {{baseUrl}}/user/stat
Authorization: Bearer {{authToken}}

View file

@ -74,7 +74,10 @@ export default function ChatBox(): JSX.Element {
useEffect(() => {
proxyFetchGet('/api/user/privacy')
.then((res) => {
const allEnabled = isPrivacyAllEnabled(res || {});
const allEnabled =
res && typeof res.all_required_granted === 'boolean'
? res.all_required_granted
: isPrivacyAllEnabled(res || {});
setPrivacy(allEnabled);
setStoredPrivacyEnabled(email, allEnabled);
})

View file

@ -63,11 +63,14 @@ export default function SettingPrivacy() {
useEffect(() => {
proxyFetchGet('/api/user/privacy')
.then((res) => {
const allEnabled = isPrivacyAllEnabled(res || {});
const allEnabled =
res && typeof res.all_required_granted === 'boolean'
? res.all_required_granted
: isPrivacyAllEnabled(res || {});
setSettings((prev) =>
prev.map((item, index) => ({
...item,
checked: !!res?.[API_FIELDS[index]],
checked: res?.[API_FIELDS[index]] || false,
}))
);
setPrivacy(allEnabled);