-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
fix: plugin pin preference sync across devices #8848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sisyphbaous-DT-Project
wants to merge
1
commit into
AstrBotDevs:master
Choose a base branch
from
Sisyphbaous-DT-Project:fix/plugin-pinned-preferences-sync
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from fastapi import APIRouter, Depends, Request | ||
|
|
||
| from astrbot.dashboard.responses import ApiError, ok | ||
| from astrbot.dashboard.schemas import PluginPinnedExtensionsRequest | ||
| from astrbot.dashboard.services.plugin_preference_service import ( | ||
| PluginPreferenceService, | ||
| ) | ||
|
|
||
| from .auth import AuthContext, require_scope | ||
|
|
||
| router = APIRouter(tags=["Plugin Preferences"]) | ||
|
|
||
|
|
||
| async def require_plugin_scope(request: Request) -> AuthContext: | ||
| """校验当前请求具有 plugin scope 权限。""" | ||
| return await require_scope(request, "plugin") | ||
|
|
||
|
|
||
| def get_plugin_preference_service(request: Request) -> PluginPreferenceService: | ||
| """从应用状态获取插件偏好服务。""" | ||
| return request.app.state.services.plugin_preferences | ||
|
|
||
|
|
||
| @router.get("/plugins/preferences/pinned") | ||
| async def get_pinned_extensions( | ||
| _auth: AuthContext = Depends(require_plugin_scope), | ||
| service: PluginPreferenceService = Depends(get_plugin_preference_service), | ||
| ): | ||
| """获取 Dashboard 全局置顶插件列表。""" | ||
| try: | ||
| pinned, preference_exists = await service.get_pinned_extensions() | ||
| except Exception as exc: | ||
| raise ApiError("加载插件置顶偏好失败", status_code=500) from exc | ||
| return ok( | ||
| { | ||
| "pinned_extensions": pinned, | ||
| "preference_exists": preference_exists, | ||
| } | ||
| ) | ||
|
|
||
|
|
||
| @router.put("/plugins/preferences/pinned") | ||
| async def set_pinned_extensions( | ||
| payload: PluginPinnedExtensionsRequest, | ||
| _auth: AuthContext = Depends(require_plugin_scope), | ||
| service: PluginPreferenceService = Depends(get_plugin_preference_service), | ||
| ): | ||
| """更新 Dashboard 全局置顶插件列表。""" | ||
| try: | ||
| pinned = await service.set_pinned_extensions(payload.pinned_extensions) | ||
| except Exception as exc: | ||
| raise ApiError("保存插件置顶偏好失败", status_code=500) from exc | ||
| return ok({"pinned_extensions": pinned, "preference_exists": True}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from astrbot.core.db import BaseDatabase | ||
|
|
||
| PREFERENCE_SCOPE = "global" | ||
| PREFERENCE_SCOPE_ID = "global" | ||
| PREFERENCE_KEY = "plugin_pinned_extensions" | ||
|
|
||
|
|
||
| class PluginPreferenceService: | ||
| """Dashboard 插件全局偏好服务。 | ||
|
|
||
| 当前仅用于持久化插件置顶顺序。数据存储在 preferences 表中, | ||
| 使用 global scope,因此是 Dashboard 全局偏好,不按登录用户隔离。 | ||
| """ | ||
|
|
||
| def __init__(self, db: BaseDatabase) -> None: | ||
| """初始化服务。 | ||
|
|
||
| Args: | ||
| db: 数据库访问对象,直接使用 BaseDatabase 的 preference 方法。 | ||
| """ | ||
| self.db = db | ||
|
|
||
| @staticmethod | ||
| def normalize_pinned_extensions(value: object) -> list[str]: | ||
| """将任意输入归一化为置顶插件名称列表。 | ||
|
|
||
| 过滤规则: | ||
| - 仅保留非空字符串; | ||
| - 去除首尾空白; | ||
| - 按出现顺序去重; | ||
| - 脏数据或非列表输入兜底为空列表。 | ||
|
|
||
| Args: | ||
| value: 待归一化的原始值。 | ||
|
|
||
| Returns: | ||
| 归一化后的插件名称列表。 | ||
| """ | ||
| if not isinstance(value, list): | ||
| return [] | ||
|
|
||
| seen: set[str] = set() | ||
| result: list[str] = [] | ||
| for item in value: | ||
| if not isinstance(item, str): | ||
| continue | ||
| name = item.strip() | ||
| if not name or name in seen: | ||
| continue | ||
| seen.add(name) | ||
| result.append(name) | ||
| return result | ||
|
|
||
| async def get_pinned_extensions(self) -> tuple[list[str], bool]: | ||
| """从数据库读取置顶插件列表。 | ||
|
|
||
| Returns: | ||
| 已归一化的置顶插件名称列表,以及该偏好记录是否存在。 | ||
| """ | ||
| preference = await self.db.get_preference( | ||
| PREFERENCE_SCOPE, | ||
| PREFERENCE_SCOPE_ID, | ||
| PREFERENCE_KEY, | ||
| ) | ||
|
|
||
| preference_exists = preference is not None | ||
| if not preference_exists or not isinstance(preference.value, dict): | ||
| return [], preference_exists | ||
|
|
||
| return self.normalize_pinned_extensions(preference.value.get("val")), True | ||
|
|
||
| async def set_pinned_extensions(self, names: object) -> list[str]: | ||
| """保存置顶插件列表到数据库。 | ||
|
|
||
| Args: | ||
| names: 待保存的插件名称列表,可为任意内容,会先归一化。 | ||
|
|
||
| Returns: | ||
| 归一化后实际保存的插件名称列表。 | ||
| """ | ||
| normalized = self.normalize_pinned_extensions(names) | ||
| await self.db.insert_preference_or_update( | ||
| PREFERENCE_SCOPE, | ||
| PREFERENCE_SCOPE_ID, | ||
| PREFERENCE_KEY, | ||
| {"val": normalized}, | ||
| ) | ||
| return normalized |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Tighten the type of
updatePinnedExtensionsparameters to reflect the expected string[] payload.The backend and OpenAPI schema expect
pinned_extensionsto be astring[], but this method currently usesnames: unknown[], weakening type safety. Since the caller already normalizes vianormalizePinnedExtensions, you can safely change this tostring[](orreadonly string[]) to better align with the server contract and catch invalid shapes at compile time.