feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
467
frontend/packages/arch/bot-store/src/auth/index.tsx
Normal file
467
frontend/packages/arch/bot-store/src/auth/index.tsx
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable @coze-arch/max-line-per-function */
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { create } from 'zustand';
|
||||
import { uniqBy, isObject } from 'lodash-es';
|
||||
import { reporter } from '@coze-arch/logger';
|
||||
import { CustomError } from '@coze-arch/bot-error';
|
||||
import { type Creator } from '@coze-arch/bot-api/playground_api';
|
||||
import {
|
||||
type ResourceIdentifier,
|
||||
ResourceType,
|
||||
PrincipalType,
|
||||
} from '@coze-arch/bot-api/permission_authz';
|
||||
import { type CollaboratorType } from '@coze-arch/bot-api/pat_permission_api';
|
||||
import {
|
||||
PlaygroundApi,
|
||||
patPermissionApi,
|
||||
workflowApi,
|
||||
intelligenceApi,
|
||||
type BotAPIRequestConfig,
|
||||
} from '@coze-arch/bot-api';
|
||||
|
||||
interface AuthStoreState {
|
||||
/* 两层map
|
||||
{
|
||||
资源类型: {
|
||||
资源ID: 协作者
|
||||
}
|
||||
}
|
||||
*/
|
||||
collaboratorsMap: Record<ResourceType, Record<string, Creator[]>>;
|
||||
}
|
||||
|
||||
interface AuthStoreAction {
|
||||
getCachedCollaborators: (resource: ResourceIdentifier) => Creator[];
|
||||
fetchCollaborators: (params: {
|
||||
spaceId: string;
|
||||
resource: ResourceIdentifier;
|
||||
}) => Promise<Creator[]>;
|
||||
removeCollaborators: (
|
||||
resource: ResourceIdentifier,
|
||||
userId: string,
|
||||
options?: BotAPIRequestConfig,
|
||||
) => Promise<void>;
|
||||
batchRemoveCollaborators: (
|
||||
resource: ResourceIdentifier,
|
||||
userIds: string[],
|
||||
options?: BotAPIRequestConfig,
|
||||
) => Promise<[string[], string[]]>;
|
||||
addCollaborator: (params: {
|
||||
resource: ResourceIdentifier;
|
||||
user: Creator;
|
||||
options?: BotAPIRequestConfig;
|
||||
roles?: Array<CollaboratorType>;
|
||||
}) => Promise<void>;
|
||||
editCollaborator: (params: {
|
||||
resource: ResourceIdentifier;
|
||||
user: Creator;
|
||||
options?: BotAPIRequestConfig;
|
||||
roles?: Array<CollaboratorType>;
|
||||
}) => Promise<void>;
|
||||
batchAddCollaborators: (params: {
|
||||
resource: ResourceIdentifier;
|
||||
users: Creator[];
|
||||
options?: BotAPIRequestConfig;
|
||||
// 第三个参数是error code
|
||||
roles?: Array<CollaboratorType>;
|
||||
}) => Promise<[Creator[], Creator[], number]>;
|
||||
// permission 服务新增的批量添加接口
|
||||
batchAddCollaboratorsServer: (params: {
|
||||
resource: ResourceIdentifier;
|
||||
users: Creator[];
|
||||
options?: BotAPIRequestConfig;
|
||||
// 第三个参数是error code
|
||||
roles?: Array<CollaboratorType>;
|
||||
}) => Promise<boolean>;
|
||||
}
|
||||
|
||||
const defaultState: AuthStoreState = {
|
||||
collaboratorsMap: Object.values(ResourceType).reduce(
|
||||
(r, val) => ({ ...r, [val]: {} }),
|
||||
{},
|
||||
) as AuthStoreState['collaboratorsMap'],
|
||||
};
|
||||
|
||||
export const useAuthStore = create<AuthStoreState & AuthStoreAction>()(
|
||||
// eslint-disable-next-line @coze-arch/zustand/devtools-config, max-lines-per-function
|
||||
devtools((set, get) => ({
|
||||
...defaultState,
|
||||
getCachedCollaborators: resource =>
|
||||
get().collaboratorsMap[resource.type][resource.id],
|
||||
//
|
||||
fetchCollaborators: async ({ spaceId, resource }) => {
|
||||
switch (resource.type) {
|
||||
case ResourceType.Bot: {
|
||||
const {
|
||||
data: { creator, collaboration_list, collaborator_roles },
|
||||
} = await PlaygroundApi.DraftBotCollaboration({
|
||||
space_id: spaceId,
|
||||
bot_id: resource.id,
|
||||
});
|
||||
|
||||
const res: Creator[] = [
|
||||
creator as Creator,
|
||||
...(collaboration_list
|
||||
? collaboration_list.map(item => ({
|
||||
...item,
|
||||
roles: collaborator_roles?.[item.id as string] ?? undefined,
|
||||
}))
|
||||
: []),
|
||||
];
|
||||
set(({ collaboratorsMap }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: res,
|
||||
},
|
||||
},
|
||||
}));
|
||||
return res;
|
||||
}
|
||||
case ResourceType.Workflow: {
|
||||
const result = await workflowApi.ListCollaborators({
|
||||
space_id: spaceId,
|
||||
workflow_id: resource.id,
|
||||
});
|
||||
const data = result.data as { owner: boolean; user: Creator }[];
|
||||
|
||||
const creator = (data ?? []).find(it => it.owner === true)?.user;
|
||||
const collaborationList = (data ?? [])
|
||||
.filter(it => it?.user?.id !== creator?.id)
|
||||
.map(item => item.user);
|
||||
|
||||
const res: Creator[] = [
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
creator,
|
||||
...(collaborationList ? collaborationList : []),
|
||||
];
|
||||
|
||||
set(({ collaboratorsMap }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: res,
|
||||
},
|
||||
},
|
||||
}));
|
||||
return res;
|
||||
}
|
||||
case ResourceType.Project: {
|
||||
const result = await intelligenceApi.ListIntelligenceCollaboration({
|
||||
intelligence_id: resource.id,
|
||||
intelligence_type: 2, // 1-Bot, 2-Project
|
||||
});
|
||||
const creator = result.data.owner_info;
|
||||
const collaborators =
|
||||
result.data.collaborator_info?.filter(
|
||||
user => user.user_id !== creator?.user_id,
|
||||
) ?? [];
|
||||
const res: Creator[] = [creator, ...collaborators]
|
||||
.filter(user => !!user)
|
||||
.map(user => ({
|
||||
id: user?.user_id,
|
||||
name: user?.nickname,
|
||||
avatar_url: user?.avatar_url,
|
||||
user_name: user?.user_unique_name,
|
||||
user_label: user?.user_label,
|
||||
}));
|
||||
set(({ collaboratorsMap }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: res,
|
||||
},
|
||||
},
|
||||
}));
|
||||
return [];
|
||||
}
|
||||
default:
|
||||
throw new CustomError(
|
||||
'',
|
||||
'unhandled resource type calling fetchCollaborators',
|
||||
);
|
||||
}
|
||||
},
|
||||
removeCollaborators: async (resource, userId, options) => {
|
||||
await patPermissionApi.RemoveCollaborator(
|
||||
{
|
||||
resource,
|
||||
principal: {
|
||||
id: userId,
|
||||
type: PrincipalType.User,
|
||||
},
|
||||
},
|
||||
options,
|
||||
);
|
||||
set(({ collaboratorsMap, getCachedCollaborators }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: getCachedCollaborators(resource).filter(
|
||||
c => c.id !== userId,
|
||||
),
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
// 暂时由前端批量处理
|
||||
batchRemoveCollaborators: async (resource, userIds, options) => {
|
||||
const resultArr = await Promise.all(
|
||||
userIds.map(
|
||||
userId =>
|
||||
new Promise<boolean>(r => {
|
||||
patPermissionApi
|
||||
.RemoveCollaborator(
|
||||
{
|
||||
resource,
|
||||
principal: {
|
||||
id: userId,
|
||||
type: PrincipalType.User,
|
||||
},
|
||||
},
|
||||
options,
|
||||
)
|
||||
.then(() => {
|
||||
r(true);
|
||||
})
|
||||
.catch(() => {
|
||||
r(false);
|
||||
});
|
||||
}),
|
||||
),
|
||||
);
|
||||
const [removedUserIds, failedUserIds] = resultArr.reduce<
|
||||
[string[], string[]]
|
||||
>(
|
||||
([r, f], success, index) => {
|
||||
const currentId = userIds[index];
|
||||
return success ? [[...r, currentId], f] : [r, [...f, currentId]];
|
||||
},
|
||||
[[], []],
|
||||
);
|
||||
set(({ collaboratorsMap, getCachedCollaborators }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: getCachedCollaborators(resource).filter(
|
||||
c => !removedUserIds.includes(c.id ?? ''),
|
||||
),
|
||||
},
|
||||
},
|
||||
}));
|
||||
return [removedUserIds, failedUserIds];
|
||||
},
|
||||
addCollaborator: async ({ resource, user, options, roles }) => {
|
||||
await patPermissionApi.AddCollaborator(
|
||||
{
|
||||
resource,
|
||||
principal: {
|
||||
id: user.id ?? '',
|
||||
type: PrincipalType.User,
|
||||
},
|
||||
collaborator_types: roles,
|
||||
},
|
||||
options,
|
||||
);
|
||||
set(({ collaboratorsMap, getCachedCollaborators }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: uniqBy(
|
||||
[
|
||||
...getCachedCollaborators(resource),
|
||||
{
|
||||
...user,
|
||||
roles,
|
||||
},
|
||||
],
|
||||
'id',
|
||||
),
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
batchAddCollaborators: async ({ resource, users, options, roles }) => {
|
||||
const resultArr = await Promise.all(
|
||||
users.map(
|
||||
user =>
|
||||
new Promise<{ result: true } | { result: false; error: unknown }>(
|
||||
r => {
|
||||
patPermissionApi
|
||||
.AddCollaborator(
|
||||
{
|
||||
resource,
|
||||
principal: {
|
||||
id: user.id ?? '',
|
||||
type: PrincipalType.User,
|
||||
},
|
||||
collaborator_types: roles,
|
||||
},
|
||||
options,
|
||||
)
|
||||
.then(() => {
|
||||
r({ result: true });
|
||||
})
|
||||
.catch(error => {
|
||||
reporter.error({
|
||||
namespace: 'collaborator',
|
||||
error,
|
||||
message: 'batchAddCollaborators error',
|
||||
meta: {
|
||||
resource,
|
||||
principal: {
|
||||
id: user.id ?? '',
|
||||
type: PrincipalType.User,
|
||||
},
|
||||
},
|
||||
});
|
||||
r({ result: false, error });
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
// 目前的批量实现需要对单个添加的接口的code进行排序,拿到最高优先级的message来透出
|
||||
let errorCode = 0;
|
||||
const [addedUsers, failedUsers] = resultArr.reduce<
|
||||
[Creator[], Creator[]]
|
||||
>(
|
||||
([r, f], finish, index) => {
|
||||
const user = users[index];
|
||||
// 这么写是为了ts能正确类型推导。ts@5.0.4
|
||||
if (finish.result === true) {
|
||||
return [[...r, user], f];
|
||||
}
|
||||
if (isObject(finish.error)) {
|
||||
const error = finish.error as {
|
||||
code: number | string;
|
||||
message?: string;
|
||||
msg?: string;
|
||||
};
|
||||
// 比较code
|
||||
if (Number(error.code) > errorCode) {
|
||||
errorCode = Number(error.code);
|
||||
}
|
||||
}
|
||||
// 错误时,需要比较code然后复制message
|
||||
return [r, [...f, user]];
|
||||
},
|
||||
[[], []],
|
||||
);
|
||||
set(({ collaboratorsMap, getCachedCollaborators }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: uniqBy(
|
||||
[
|
||||
...getCachedCollaborators(resource),
|
||||
...addedUsers.map(item => ({
|
||||
...item,
|
||||
roles,
|
||||
})),
|
||||
],
|
||||
'id',
|
||||
),
|
||||
},
|
||||
},
|
||||
}));
|
||||
return [addedUsers, failedUsers, errorCode];
|
||||
},
|
||||
|
||||
batchAddCollaboratorsServer: async ({
|
||||
resource,
|
||||
users,
|
||||
options,
|
||||
roles,
|
||||
}) => {
|
||||
const { code } = await patPermissionApi.BatchAddCollaborator(
|
||||
{
|
||||
principal_type: 1,
|
||||
resource,
|
||||
principal_ids: users.map(user => user.id).filter(Boolean) as string[],
|
||||
},
|
||||
options,
|
||||
);
|
||||
if (code === 0) {
|
||||
set(({ collaboratorsMap, getCachedCollaborators }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: uniqBy(
|
||||
[
|
||||
...getCachedCollaborators(resource),
|
||||
...users.map(item => ({
|
||||
...item,
|
||||
roles,
|
||||
})),
|
||||
],
|
||||
'id',
|
||||
),
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
return code === 0;
|
||||
},
|
||||
|
||||
editCollaborator: async ({ resource, user, options, roles }) => {
|
||||
await patPermissionApi.ModifyCollaborator(
|
||||
{
|
||||
resource,
|
||||
principal: {
|
||||
id: user.id ?? '',
|
||||
type: PrincipalType.User,
|
||||
},
|
||||
collaborator_types: roles,
|
||||
},
|
||||
options,
|
||||
);
|
||||
set(({ collaboratorsMap, getCachedCollaborators }) => ({
|
||||
collaboratorsMap: {
|
||||
...collaboratorsMap,
|
||||
[resource.type]: {
|
||||
...collaboratorsMap[resource.type],
|
||||
[resource.id]: uniqBy(
|
||||
[
|
||||
...getCachedCollaborators(resource).map(item => {
|
||||
if (item.id === user.id) {
|
||||
return {
|
||||
...item,
|
||||
roles,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
],
|
||||
'id',
|
||||
),
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
})),
|
||||
);
|
||||
229
frontend/packages/arch/bot-store/src/data_item.d.ts
vendored
Normal file
229
frontend/packages/arch/bot-store/src/data_item.d.ts
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare namespace DataItem {
|
||||
interface UserConnectItem {
|
||||
platform: string;
|
||||
profile_image_url: string;
|
||||
expired_time: number;
|
||||
expires_in: number;
|
||||
platform_screen_name: string;
|
||||
user_id: number;
|
||||
platform_uid: string;
|
||||
sec_platform_uid: string;
|
||||
platform_app_id: number;
|
||||
modify_time: number;
|
||||
access_token: string;
|
||||
open_id: string;
|
||||
}
|
||||
|
||||
interface UserInfo {
|
||||
app_id: number;
|
||||
/**
|
||||
* @deprecated 会因为溢出丢失精度,使用 user_id_str
|
||||
*/
|
||||
user_id: number;
|
||||
user_id_str: string;
|
||||
odin_user_type: number;
|
||||
name: string;
|
||||
screen_name: string;
|
||||
avatar_url: string;
|
||||
user_verified: boolean;
|
||||
email?: string;
|
||||
email_collected: boolean;
|
||||
expend_attrs?: Record<string, unknown>;
|
||||
phone_collected: boolean;
|
||||
verified_content: string;
|
||||
verified_agency: string;
|
||||
is_blocked: number;
|
||||
is_blocking: number;
|
||||
bg_img_url: string;
|
||||
gender: number;
|
||||
media_id: number;
|
||||
user_auth_info: string;
|
||||
industry: string;
|
||||
area: string;
|
||||
can_be_found_by_phone: number;
|
||||
mobile: string;
|
||||
birthday: string;
|
||||
description: string;
|
||||
status: number;
|
||||
new_user: number;
|
||||
first_login_app: number;
|
||||
session_key: string;
|
||||
is_recommend_allowed: number;
|
||||
recommend_hint_message: string;
|
||||
followings_count: number;
|
||||
followers_count: number;
|
||||
visit_count_recent: number;
|
||||
skip_edit_profile: number;
|
||||
is_manual_set_user_info: boolean;
|
||||
device_id: number;
|
||||
country_code: number;
|
||||
has_password: number;
|
||||
share_to_repost: number;
|
||||
user_decoration: string;
|
||||
user_privacy_extend: number;
|
||||
old_user_id: number;
|
||||
old_user_id_str: string;
|
||||
sec_user_id: string;
|
||||
sec_old_user_id: string;
|
||||
vcd_account: number;
|
||||
vcd_relation: number;
|
||||
can_bind_visitor_account: boolean;
|
||||
is_visitor_account: boolean;
|
||||
is_only_bind_ins: boolean;
|
||||
user_device_record_status: number;
|
||||
is_kids_mode: number;
|
||||
source: string;
|
||||
is_employee: boolean;
|
||||
passport_enterprise_user_type: number;
|
||||
need_device_create: number;
|
||||
need_ttwid_migration: number;
|
||||
user_auth_status: number;
|
||||
user_safe_mobile_2fa: string;
|
||||
safe_mobile_country_code: number;
|
||||
lite_user_info_string: string;
|
||||
lite_user_info_demotion: number;
|
||||
app_user_info: {
|
||||
user_unique_name?: string;
|
||||
};
|
||||
need_check_bind_status: boolean;
|
||||
bui_audit_info?: {
|
||||
audit_info: {
|
||||
user_unique_name?: string;
|
||||
avatar_url?: string;
|
||||
name?: string;
|
||||
[key?: string]: unknown;
|
||||
}; // Record<string, unknown>;
|
||||
// int值。1审核中,2审核通过,3审核不通过
|
||||
audit_status: number;
|
||||
details: Record<string, unknown>;
|
||||
is_auditing: boolean;
|
||||
last_update_time: number;
|
||||
unpass_reason: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证码的返回数据结构
|
||||
*/
|
||||
interface SendCodeData {
|
||||
mobile: string;
|
||||
mobile_ticket: string;
|
||||
retry_time: number;
|
||||
}
|
||||
|
||||
interface bindWithEmailLoginParams {
|
||||
access_token?: string;
|
||||
access_token_secret?: string;
|
||||
code?: string;
|
||||
openid?: number;
|
||||
profile_key?: string;
|
||||
platform_app_id: number;
|
||||
redirect_uri?: string;
|
||||
extra_params?: object;
|
||||
}
|
||||
|
||||
interface UserCheckResponse {
|
||||
app_user_info?: null;
|
||||
authType: number;
|
||||
error_code?: number;
|
||||
in_old_process?: boolean;
|
||||
oauth_platforms?: string[] | null;
|
||||
platform_user_names?: Record<string, unknown>;
|
||||
userType?: number;
|
||||
value_ticket: string;
|
||||
}
|
||||
|
||||
interface ValidateCodeResponse {
|
||||
ticket: string;
|
||||
}
|
||||
|
||||
interface AuthorizeResponse {
|
||||
token: string;
|
||||
user_info: {
|
||||
user_id: number;
|
||||
app_id: number;
|
||||
user_name: string;
|
||||
screen_name: string;
|
||||
mobile: string;
|
||||
email: string;
|
||||
avatar_url: string;
|
||||
description: string;
|
||||
create_time: number;
|
||||
is_new_user: boolean;
|
||||
is_new_connect: boolean;
|
||||
session_key: string;
|
||||
session_app_id: number;
|
||||
safe_mobile: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AuthLoginParams {
|
||||
platform_app_id: number;
|
||||
code?: string;
|
||||
access_token?: string;
|
||||
access_token_secret?: string;
|
||||
openid?: string;
|
||||
profile_key?: string;
|
||||
login_only?: boolean;
|
||||
extra_params?: object;
|
||||
}
|
||||
interface bindWithEmailLoginParams {
|
||||
access_token?: string;
|
||||
access_token_secret?: string;
|
||||
code?: string;
|
||||
openid?: number;
|
||||
profile_key?: string;
|
||||
platform_app_id: number;
|
||||
redirect_uri?: string;
|
||||
extra_params?: object;
|
||||
}
|
||||
|
||||
interface bindWithMobileLoginParams {
|
||||
code?: string;
|
||||
access_token?: string;
|
||||
platform_app_id: number;
|
||||
platform: string;
|
||||
need_mobile?: 0 | 1;
|
||||
check_mobile?: 0 | 1;
|
||||
change_bind?: 0 | 1;
|
||||
}
|
||||
|
||||
interface ResetByEmailTicket {
|
||||
ticket: string;
|
||||
}
|
||||
interface AuditItem {
|
||||
pass: boolean;
|
||||
title: string;
|
||||
text: string[];
|
||||
reason: string[] | null;
|
||||
}
|
||||
|
||||
interface CancelCheckResponse {
|
||||
business_audit: AuditItem;
|
||||
cancel_ticket: string;
|
||||
common_audit: AuditItem;
|
||||
error_code: number;
|
||||
protocol: string;
|
||||
punish_audit: AuditItem;
|
||||
user_permission_audit: AuditItem;
|
||||
}
|
||||
interface UploadAvatarResponse {
|
||||
web_uri: string;
|
||||
}
|
||||
}
|
||||
31
frontend/packages/arch/bot-store/src/index.ts
Normal file
31
frontend/packages/arch/bot-store/src/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
/** @deprecated 该使用方式已废弃,后续请使用@coze-arch/foundation-sdk导出的方法*/
|
||||
useSpaceStore,
|
||||
/** @deprecated 该使用方式已废弃,后续请使用@coze-arch/foundation-sdk导出的方法*/
|
||||
useSpace,
|
||||
/** @deprecated 该使用方式已废弃,后续请使用@coze-arch/foundation-sdk导出的方法*/
|
||||
useSpaceList,
|
||||
} from '@coze-foundation/space-store';
|
||||
|
||||
export { useAuthStore } from './auth';
|
||||
|
||||
/** @deprecated - 持久化方案有问题,废弃 */
|
||||
export { clearStorage } from './utils/get-storage';
|
||||
|
||||
export { useSpaceGrayStore, TccKey } from './space-gray';
|
||||
84
frontend/packages/arch/bot-store/src/space-gray/index.ts
Normal file
84
frontend/packages/arch/bot-store/src/space-gray/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { create } from 'zustand';
|
||||
import { reporter } from '@coze-arch/logger';
|
||||
import { type WorkflowGrayFeatureItem } from '@coze-arch/bot-api/developer_api';
|
||||
import { workflowApi } from '@coze-arch/bot-api';
|
||||
|
||||
export enum TccKey {
|
||||
ImageGenerateConverter = 'ImageGenerateConverter',
|
||||
}
|
||||
|
||||
interface TccStore {
|
||||
spaceId: string;
|
||||
grayFeatureItems: Array<WorkflowGrayFeatureItem>;
|
||||
}
|
||||
|
||||
interface TccAction {
|
||||
load: (spaceId: string) => Promise<void>;
|
||||
isHitSpaceGray: (key: TccKey) => boolean;
|
||||
}
|
||||
|
||||
const initialStore: TccStore = {
|
||||
spaceId: '',
|
||||
grayFeatureItems: [],
|
||||
};
|
||||
|
||||
const fetchTccConfig = async spaceId => {
|
||||
try {
|
||||
const getWorkflowGrayFeature = IS_BOT_OP
|
||||
? workflowApi.OPGetWorkflowGrayFeature.bind(workflowApi)
|
||||
: workflowApi.GetWorkflowGrayFeature.bind(workflowApi);
|
||||
const { data } = await getWorkflowGrayFeature({
|
||||
space_id: spaceId,
|
||||
});
|
||||
return data;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
reporter.error({
|
||||
message: 'workflow_prefetch_tcc_fail',
|
||||
namespace: 'workflow',
|
||||
error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* 通过 tcc 动态配置的 space 粒度的灰度 */
|
||||
export const useSpaceGrayStore = create<TccStore & TccAction>()(
|
||||
devtools(
|
||||
(set, get) => ({
|
||||
...initialStore,
|
||||
load: async spaceId => {
|
||||
const { spaceId: cachedSpaceId } = get();
|
||||
if (spaceId !== cachedSpaceId) {
|
||||
const data = await fetchTccConfig(spaceId);
|
||||
set({ grayFeatureItems: data, spaceId });
|
||||
}
|
||||
},
|
||||
isHitSpaceGray: key => {
|
||||
const { grayFeatureItems } = get();
|
||||
return !!(grayFeatureItems || []).find(item => item.feature === key)
|
||||
?.in_gray;
|
||||
},
|
||||
}),
|
||||
{
|
||||
enabled: IS_DEV_MODE,
|
||||
name: 'botStudio.TccStore',
|
||||
},
|
||||
),
|
||||
);
|
||||
18
frontend/packages/arch/bot-store/src/type.d.ts
vendored
Normal file
18
frontend/packages/arch/bot-store/src/type.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare const IS_BOT_OP: boolean;
|
||||
declare const IS_DEV_MODE: boolean;
|
||||
46
frontend/packages/arch/bot-store/src/utils/get-storage.ts
Normal file
46
frontend/packages/arch/bot-store/src/utils/get-storage.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { type StateStorage } from 'zustand/middleware';
|
||||
import { throttle } from 'lodash-es';
|
||||
import localForage from 'localforage';
|
||||
|
||||
const instance = localForage.createInstance({
|
||||
name: 'botStudio',
|
||||
storeName: 'botStudio',
|
||||
});
|
||||
|
||||
const throttleTime = 1000;
|
||||
|
||||
/**
|
||||
* 获取store数据持久化引擎
|
||||
*/
|
||||
export const getStorage = (): StateStorage => {
|
||||
const persistStorage: StateStorage = {
|
||||
getItem: async (name: string) => await instance.getItem(name),
|
||||
setItem: throttle(async (name: string, value: unknown): Promise<void> => {
|
||||
await instance.setItem(name, value);
|
||||
}, throttleTime),
|
||||
removeItem: async (name: string) => {
|
||||
await instance.removeItem(name);
|
||||
},
|
||||
};
|
||||
|
||||
return persistStorage;
|
||||
};
|
||||
|
||||
/** @deprecated - 持久化方案有问题,废弃 */
|
||||
export const clearStorage = instance.clear;
|
||||
Reference in New Issue
Block a user