feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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 { useEffect, useState } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
import { Modal, type FormApi } from '@coze-arch/coze-design';
|
||||
|
||||
import { ExpirationDate, getExpireAt } from '@/utils/time';
|
||||
import {
|
||||
usePATPermission,
|
||||
useCreatePAT,
|
||||
useUpdatePAT,
|
||||
} from '@/hooks/pat/use-token';
|
||||
|
||||
export interface FormApiInfo {
|
||||
name: string;
|
||||
duration_day: ExpirationDate;
|
||||
|
||||
expire_at: Date;
|
||||
}
|
||||
|
||||
interface PatFormProps {
|
||||
editInfo?: PersonalAccessToken;
|
||||
isCreate: boolean;
|
||||
isShowAuthMigrateNotice?: boolean;
|
||||
formApi: React.MutableRefObject<FormApi<FormApiInfo> | undefined>;
|
||||
validateCustomParams?: () => boolean;
|
||||
getCustomParams?: () => Record<string, unknown>;
|
||||
afterSubmit?: (params: Record<string, unknown>) => void;
|
||||
}
|
||||
|
||||
const getDurationData = (durationDay: ExpirationDate, expireAt: Date) => ({
|
||||
duration_day: durationDay,
|
||||
...(durationDay === ExpirationDate.CUSTOMIZE
|
||||
? { expire_at: getExpireAt(expireAt as Date) }
|
||||
: {}),
|
||||
});
|
||||
|
||||
const validateName = (name?: string) => Boolean(name);
|
||||
const validateDuration = (durationDay?: ExpirationDate, expireAt?: Date) => {
|
||||
if (!durationDay) {
|
||||
return false;
|
||||
}
|
||||
if (durationDay === ExpirationDate.CUSTOMIZE && !expireAt) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const authMigrateNoticeLSKey = 'auth_migrate_notice_do_not_show_again';
|
||||
|
||||
const useAuthMigrateNotice = (isShowAuthMigrateNotice?: boolean) => {
|
||||
useEffect(() => {
|
||||
if (!isShowAuthMigrateNotice) {
|
||||
return;
|
||||
}
|
||||
if (!localStorage.getItem(authMigrateNoticeLSKey)) {
|
||||
Modal.info({
|
||||
title: I18n.t('api_permissionkey_notification_title'),
|
||||
content: I18n.t('api_permissionkey_notification_content'),
|
||||
okText: I18n.t('got_it'),
|
||||
onOk: () => {
|
||||
localStorage.setItem(authMigrateNoticeLSKey, 'true');
|
||||
},
|
||||
showCancelButton: false,
|
||||
closable: false,
|
||||
maskClosable: false,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const usePatForm = ({
|
||||
editInfo,
|
||||
isCreate,
|
||||
formApi,
|
||||
getCustomParams,
|
||||
validateCustomParams,
|
||||
afterSubmit,
|
||||
isShowAuthMigrateNotice,
|
||||
}: PatFormProps) => {
|
||||
const { patPermission } = usePATPermission({
|
||||
patId: editInfo?.id,
|
||||
});
|
||||
|
||||
const { loading: createLoading, runCreate, successData } = useCreatePAT();
|
||||
const {
|
||||
loading: updateLoading,
|
||||
runUpdate,
|
||||
updateSuccessData,
|
||||
} = useUpdatePAT();
|
||||
|
||||
const [isFailToValid, setIsFailToValid] = useState(true);
|
||||
|
||||
const onSubmit = () => {
|
||||
const {
|
||||
name = '',
|
||||
duration_day,
|
||||
expire_at,
|
||||
} = formApi.current?.getValues() || {};
|
||||
|
||||
const params = {
|
||||
name,
|
||||
...(getCustomParams?.() || {}),
|
||||
};
|
||||
if (isCreate) {
|
||||
runCreate({
|
||||
...params,
|
||||
...getDurationData(duration_day as ExpirationDate, expire_at as Date),
|
||||
});
|
||||
} else {
|
||||
runUpdate({ ...params, id: editInfo?.id ?? '' });
|
||||
}
|
||||
afterSubmit?.({ ...params, duration_day, expire_at });
|
||||
};
|
||||
|
||||
const validateParams = () => {
|
||||
const { name, duration_day, expire_at } =
|
||||
formApi.current?.getValues() || {};
|
||||
|
||||
const nameValid = validateName(name);
|
||||
const isCustomParamsValid = validateCustomParams?.() !== false;
|
||||
const durationValid = isCreate
|
||||
? validateDuration(duration_day, expire_at)
|
||||
: true;
|
||||
setIsFailToValid(!(nameValid && isCustomParamsValid && durationValid));
|
||||
};
|
||||
|
||||
const onFormValueChange = (
|
||||
_values: FormApiInfo,
|
||||
_changedValue: FormApiInfo,
|
||||
) => {
|
||||
validateParams();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isCreate) {
|
||||
formApi.current?.setValue('name', 'Secret token');
|
||||
} else if (patPermission && patPermission?.personal_access_token?.name) {
|
||||
formApi.current?.setValue(
|
||||
'name',
|
||||
patPermission?.personal_access_token?.name,
|
||||
);
|
||||
}
|
||||
}, [patPermission]);
|
||||
|
||||
const ready = isCreate ? true : !!patPermission;
|
||||
|
||||
useAuthMigrateNotice(isShowAuthMigrateNotice);
|
||||
|
||||
return {
|
||||
isFailToValid,
|
||||
ready,
|
||||
loading: updateLoading || createLoading,
|
||||
onSubmit,
|
||||
onFormValueChange,
|
||||
patPermission,
|
||||
validateParams,
|
||||
successData,
|
||||
updateSuccessData,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 { useState } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Toast } from '@coze-arch/coze-design';
|
||||
import {
|
||||
type CreatePersonalAccessTokenAndPermissionResponseData,
|
||||
type PersonalAccessToken,
|
||||
} from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import {
|
||||
useDeletePAT,
|
||||
useGetPATList,
|
||||
type FetchCustomPatList,
|
||||
} from '../use-token';
|
||||
export const usePatOperation = ({
|
||||
fetchCustomPatList,
|
||||
afterCancelPermissionModal,
|
||||
}: {
|
||||
fetchCustomPatList?: FetchCustomPatList;
|
||||
afterCancelPermissionModal?: (isCreate: boolean) => void;
|
||||
}) => {
|
||||
const { loading, dataSource, fetchData } = useGetPATList({
|
||||
fetchCustomPatList,
|
||||
});
|
||||
const { runDelete } = useDeletePAT({
|
||||
successHandle: () => {
|
||||
Toast.success({ content: I18n.t('Delete_success'), showClose: false });
|
||||
fetchData();
|
||||
},
|
||||
});
|
||||
const [showDataForm, setShowDataForm] = useState(false);
|
||||
const [showResult, setShowResult] = useState(false);
|
||||
const [isCreate, setIsCreate] = useState(true);
|
||||
const [editInfo, setEditInfo] = useState<PersonalAccessToken>();
|
||||
const [successData, setSuccessData] =
|
||||
useState<CreatePersonalAccessTokenAndPermissionResponseData>();
|
||||
const onAddClick = () => {
|
||||
setIsCreate(true);
|
||||
setShowDataForm(true);
|
||||
};
|
||||
const editHandle = (v: PersonalAccessToken) => {
|
||||
setEditInfo(v);
|
||||
setIsCreate(false);
|
||||
setShowDataForm(true);
|
||||
};
|
||||
const onCancel = () => {
|
||||
setShowDataForm(false);
|
||||
setEditInfo(undefined);
|
||||
afterCancelPermissionModal?.(isCreate);
|
||||
};
|
||||
|
||||
const createSuccessHandle = (
|
||||
data: CreatePersonalAccessTokenAndPermissionResponseData,
|
||||
) => {
|
||||
setSuccessData(data);
|
||||
setEditInfo(undefined);
|
||||
setShowResult(true);
|
||||
};
|
||||
const refreshHandle = () => {
|
||||
fetchData();
|
||||
setShowDataForm(false);
|
||||
setEditInfo(undefined);
|
||||
};
|
||||
return {
|
||||
dataSource,
|
||||
loading,
|
||||
showDataForm,
|
||||
setShowDataForm,
|
||||
isCreate,
|
||||
editInfo,
|
||||
successData,
|
||||
onAddClick,
|
||||
createSuccessHandle,
|
||||
refreshHandle,
|
||||
editHandle,
|
||||
runDelete,
|
||||
onCancel,
|
||||
setIsCreate,
|
||||
showResult,
|
||||
setShowResult,
|
||||
fetchData,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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 { useEffect, useState } from 'react';
|
||||
|
||||
import { useMemoizedFn, useRequest } from 'ahooks';
|
||||
import { REPORT_EVENTS } from '@coze-arch/report-events';
|
||||
import { reporter } from '@coze-arch/logger';
|
||||
import {
|
||||
type PersonalAccessToken,
|
||||
type CreatePersonalAccessTokenAndPermissionRequest,
|
||||
type UpdatePersonalAccessTokenAndPermissionRequest,
|
||||
type CreatePersonalAccessTokenAndPermissionResponseData,
|
||||
type GetPersonalAccessTokenAndPermissionResponseData,
|
||||
type ListPersonalAccessTokensResponse2,
|
||||
} from '@coze-arch/bot-api/pat_permission_api';
|
||||
import { patPermissionApi } from '@coze-arch/bot-api';
|
||||
|
||||
export type FetchCustomPatList =
|
||||
() => Promise<ListPersonalAccessTokensResponse2>;
|
||||
export const useGetPATList = ({
|
||||
fetchCustomPatList,
|
||||
}: {
|
||||
fetchCustomPatList?: FetchCustomPatList;
|
||||
}) => {
|
||||
const [dataSource, setDataSource] = useState<PersonalAccessToken[]>([]);
|
||||
const fetchPatList = useMemoizedFn(() => {
|
||||
if (fetchCustomPatList) {
|
||||
return fetchCustomPatList();
|
||||
}
|
||||
return patPermissionApi.ListPersonalAccessTokens({});
|
||||
});
|
||||
const { loading, run: fetchData } = useRequest(fetchPatList, {
|
||||
manual: true,
|
||||
onSuccess: dataSourceData => {
|
||||
setDataSource(dataSourceData?.data?.personal_access_tokens);
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openGetPatList,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'ListPersonalAccessTokens',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openGetPatList,
|
||||
error,
|
||||
meta: {
|
||||
action: 'ListPersonalAccessTokens',
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
dataSource,
|
||||
loading,
|
||||
fetchData,
|
||||
};
|
||||
};
|
||||
|
||||
export const useCreatePAT = () => {
|
||||
const [successData, setSuccessData] =
|
||||
useState<CreatePersonalAccessTokenAndPermissionResponseData>();
|
||||
const { loading, run: runCreate } = useRequest(
|
||||
(info: CreatePersonalAccessTokenAndPermissionRequest) =>
|
||||
patPermissionApi.CreatePersonalAccessTokenAndPermission(info),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: dataSourceData => {
|
||||
setSuccessData(dataSourceData?.data);
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'CreatePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
error,
|
||||
meta: {
|
||||
action: 'CreatePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
return {
|
||||
runCreate,
|
||||
loading,
|
||||
successData,
|
||||
};
|
||||
};
|
||||
|
||||
export const useUpdatePAT = (
|
||||
handle: {
|
||||
successHandle?: () => void;
|
||||
} = {},
|
||||
) => {
|
||||
const {
|
||||
loading,
|
||||
run: runUpdate,
|
||||
data: updateSuccessData,
|
||||
} = useRequest(
|
||||
(info: UpdatePersonalAccessTokenAndPermissionRequest) =>
|
||||
patPermissionApi.UpdatePersonalAccessTokenAndPermission(info),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: () => {
|
||||
handle?.successHandle?.();
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'UpdatePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
error,
|
||||
meta: {
|
||||
action: 'UpdatePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
return {
|
||||
runUpdate,
|
||||
loading,
|
||||
updateSuccessData,
|
||||
};
|
||||
};
|
||||
|
||||
export const useDeletePAT = ({
|
||||
successHandle,
|
||||
}: {
|
||||
successHandle: () => void;
|
||||
}) => {
|
||||
const { loading, runAsync } = useRequest(
|
||||
(id: string) =>
|
||||
patPermissionApi.DeletePersonalAccessTokenAndPermission({ id }),
|
||||
{
|
||||
manual: true,
|
||||
},
|
||||
);
|
||||
const runDelete = async (id: string) => {
|
||||
try {
|
||||
await runAsync(id);
|
||||
successHandle();
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'DeletePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
error: error as Error,
|
||||
meta: {
|
||||
action: 'DeletePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
runDelete,
|
||||
loading,
|
||||
};
|
||||
};
|
||||
export const usePATPermission = ({ patId }: { patId?: string }) => {
|
||||
const [patPermission, setPatPermission] =
|
||||
useState<GetPersonalAccessTokenAndPermissionResponseData>();
|
||||
|
||||
const { error: detailError, run } = useRequest(
|
||||
(id: string) =>
|
||||
patPermissionApi.GetPersonalAccessTokenAndPermission({ id }),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: dataSourceData => {
|
||||
setPatPermission(dataSourceData.data);
|
||||
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openGetPatList,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'GetPersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openGetPatList,
|
||||
error,
|
||||
meta: {
|
||||
action: 'GetPersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
useEffect(() => {
|
||||
if (patId) {
|
||||
run(patId);
|
||||
} else {
|
||||
setPatPermission(undefined);
|
||||
}
|
||||
}, [patId]);
|
||||
|
||||
return {
|
||||
patPermission,
|
||||
detailError,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 { useState, useEffect } from 'react';
|
||||
|
||||
export const useTableHeight = (tableRef: React.RefObject<HTMLDivElement>) => {
|
||||
const [tableHeight, setTableHeight] = useState<string>('calc(100vh - 360px)');
|
||||
|
||||
useEffect(() => {
|
||||
if (!tableRef.current) {
|
||||
return;
|
||||
}
|
||||
const calculateHeight = () => {
|
||||
if (tableRef.current) {
|
||||
const topPosition = tableRef.current.getBoundingClientRect().top;
|
||||
setTableHeight(`calc(100vh - ${topPosition + 80}px)`);
|
||||
}
|
||||
};
|
||||
|
||||
calculateHeight();
|
||||
window.addEventListener('resize', calculateHeight);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', calculateHeight);
|
||||
};
|
||||
}, [tableRef.current]);
|
||||
|
||||
return tableHeight;
|
||||
};
|
||||
Reference in New Issue
Block a user