Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit12a63d4

Browse files
allow user to create/delete api keys
1 parent27ed733 commit12a63d4

File tree

14 files changed

+461
-13
lines changed

14 files changed

+461
-13
lines changed

‎client/packages/lowcoder/src/api/userApi.ts‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ export interface GetUserResponse extends ApiResponse {
3838
}&BaseUserInfo;
3939
}
4040

41+
exportinterfaceApiKeyPayload{
42+
name:string;
43+
description?:string;
44+
}
45+
46+
exportinterfaceFetchApiKeysResponseextendsApiResponse{
47+
data:{
48+
id:string;
49+
name:string;
50+
description:string;
51+
token:string;
52+
}
53+
}
54+
4155
exporttypeGetCurrentUserResponse=GenericApiResponse<CurrentUser>;
4256

4357
classUserApiextendsApi{
@@ -55,6 +69,9 @@ class UserApi extends Api {
5569
staticmarkUserStatusURL="/users/mark-status";
5670
staticuserDetailURL=(id:string)=>`/users/userDetail/${id}`;
5771
staticresetPasswordURL=`/users/reset-password`;
72+
staticfetchApiKeysURL=`/auth/api-keys`;
73+
staticcreateApiKeyURL=`/auth/api-key`;
74+
staticdeleteApiKeyURL=(id:string)=>`/auth/api-key/${id}`;
5875

5976
staticthirdPartyLogin(
6077
request:ThirdPartyAuthRequest&CommonLoginParam
@@ -120,6 +137,24 @@ class UserApi extends Api {
120137
staticresetPassword(userId:string):AxiosPromise<ApiResponse>{
121138
returnApi.post(UserApi.resetPasswordURL,{userId:userId});
122139
}
140+
141+
staticcreateApiKey({
142+
name,
143+
description=''
144+
}:ApiKeyPayload):AxiosPromise<ApiResponse>{
145+
returnApi.post(UserApi.createApiKeyURL,{
146+
name,
147+
description
148+
});
149+
}
150+
151+
staticfetchApiKeys():AxiosPromise<ApiResponse>{
152+
returnApi.get(UserApi.fetchApiKeysURL);
153+
}
154+
155+
staticdeleteApiKey(apiKeyId:string):AxiosPromise<ApiResponse>{
156+
returnApi.delete(UserApi.deleteApiKeyURL(apiKeyId));
157+
}
123158
}
124159

125160
exportdefaultUserApi;

‎client/packages/lowcoder/src/constants/reduxActionConstants.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ export const ReduxActionTypes = {
77
FETCH_CURRENT_USER_SUCCESS:"FETCH_CURRENT_USER_SUCCESS",
88
FETCH_RAW_CURRENT_USER:"FETCH_RAW_CURRENT_USER",
99
FETCH_RAW_CURRENT_USER_SUCCESS:"FETCH_RAW_CURRENT_USER_SUCCESS",
10+
FETCH_API_KEYS:"FETCH_API_KEYS",
11+
FETCH_API_KEYS_SUCCESS:"FETCH_API_KEYS_SUCCESS",
12+
1013

1114
/* plugin RELATED */
1215
FETCH_DATA_SOURCE_TYPES:"FETCH_DATA_SOURCE_TYPES",

‎client/packages/lowcoder/src/constants/userConstants.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,11 @@ export const defaultCurrentUser: CurrentUser = {
8383
extra:{},
8484
};
8585

86+
exporttypeApiKey={
87+
id:string;
88+
name:string;
89+
description:string;
90+
token:string;
91+
}
92+
8693
exporttypeUserStatusType=keyofBaseUserInfo["userStatus"];

‎client/packages/lowcoder/src/i18n/locales/de.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2261,7 +2261,15 @@ export const de: typeof en = {
22612261
"createdApps":"Erstellte Apps",
22622262
"createdModules":"Erstellte Module",
22632263
"onMarketplace":"Auf dem Marktplatz",
2264-
"howToPublish":"So veröffentlichen Sie auf dem Marktplatz"
2264+
"howToPublish":"So veröffentlichen Sie auf dem Marktplatz",
2265+
"apiKeys":"API-Schlüssel",
2266+
"createApiKey":"API-Schlüssel erstellen",
2267+
"apiKeyName":"Name",
2268+
"apiKeyDescription":"Beschreibung",
2269+
"apiKey":"API-Schlüssel",
2270+
"deleteApiKey":"API-Schlüssel löschen",
2271+
"deleteApiKeyContent":"Sind Sie sicher, dass Sie diesen API-Schlüssel löschen möchten?",
2272+
"deleteApiKeyError":"Etwas ist schief gelaufen. Bitte versuche es erneut."
22652273
},
22662274
"shortcut":{
22672275
...en.shortcut,

‎client/packages/lowcoder/src/i18n/locales/en.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2314,7 +2314,15 @@ export const en = {
23142314
"createdApps":"Created Apps",
23152315
"createdModules":"Created Modules",
23162316
"onMarketplace":"On Marketplace",
2317-
"howToPublish":"How to publish on Marketplace"
2317+
"howToPublish":"How to publish on Marketplace",
2318+
"apiKeys":"API Keys",
2319+
"createApiKey":"Create API Key",
2320+
"apiKeyName":"Name",
2321+
"apiKeyDescription":"Description",
2322+
"apiKey":"API Key",
2323+
"deleteApiKey":"Delete API Key",
2324+
"deleteApiKeyContent":"Are you sure you want to delete this API key?",
2325+
"deleteApiKeyError":"Something went wrong. Please try again."
23182326
},
23192327
"shortcut":{
23202328
"shortcutList":"Keyboard Shortcuts",

‎client/packages/lowcoder/src/i18n/locales/zh.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,6 +2148,14 @@ profile: {
21482148
createdModules:"创建的模块",
21492149
onMarketplace:"在市场上",
21502150
howToPublish:"如何在 Marketplace 上发布",
2151+
apiKeys:"API 密钥",
2152+
createApiKey:"创建 API 密钥",
2153+
apiKeyName:"姓名",
2154+
apiKeyDescription:"描述",
2155+
apiKey:"API密钥",
2156+
deleteApiKey:"删除 API 密钥",
2157+
deleteApiKeyContent:"您确定要删除此 API 密钥吗?",
2158+
deleteApiKeyError:"出了些问题。请再试一次。"
21512159
},
21522160
shortcut:{
21532161
shortcutList:"键盘快捷键",

‎client/packages/lowcoder/src/pages/ApplicationV2/UserProfileLayout.tsx‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
importReactfrom'react';
21
importCountUpfrom'react-countup';
32
import{useSelector}from"react-redux";
43
importstyledfrom"styled-components";
@@ -20,7 +19,7 @@ import { ALL_APPLICATIONS_URL } from "constants/routesURL";
2019
import{USER_PROFILE_URL}from"constants/routesURL";
2120
import{defaultasDivider}from"antd/es/divider";
2221

23-
import{Avatar,Badge,Button,Card,Col,Row,Space,Typography,Select}from'antd';
22+
import{Avatar,Badge,Button,Card,Col,Row,Space,Typography,Select,Table,Flex}from"antd";
2423

2524
import{
2625
BlurFinishInput,
@@ -40,6 +39,7 @@ import { updateUserAction, updateUserSuccess } from "redux/reduxActions/userActi
4039
import{defaultasUpload,UploadChangeParam}from"antd/es/upload";
4140
import{USER_HEAD_UPLOAD_URL}from"constants/apiConstants";
4241
import{messageInstance}from"lowcoder-design/src/components/GlobalInstances";
42+
importUserApiKeysCardfrom'./components/UserApiKeysCard';
4343

4444
const{ Text, Title, Link}=Typography;
4545
const{ Option}=Select;
@@ -161,7 +161,9 @@ export function UserProfileLayout(props: UserProfileLayoutProps) {
161161
constmodules=useSelector(modulesSelector);
162162
constorgUsers=useSelector(getOrgUsers);
163163
constorgGroups=useSelector(getOrgGroups);
164+
164165
constcurrentOrgId=user.currentOrgId;
166+
165167
constcurrentOrg=useMemo(
166168
()=>user.orgs.find((o)=>o.id===currentOrgId),
167169
[user,currentOrgId]
@@ -192,9 +194,6 @@ export function UserProfileLayout(props: UserProfileLayoutProps) {
192194
},1000);
193195
};
194196

195-
console.log("App Language",language);
196-
console.log("User Language",currentUser.uiLanguage);
197-
198197
if(!user.currentOrgId){
199198
returnnull;
200199
}
@@ -395,7 +394,9 @@ export function UserProfileLayout(props: UserProfileLayoutProps) {
395394

396395
</Space>
397396
</Card>
398-
397+
398+
<UserApiKeysCard/>
399+
399400
</ProfileView>
400401
</ContentWrapper>
401402
</Wrapper>
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1-
import{useSelector}from"react-redux";
1+
import{useDispatch,useSelector}from"react-redux";
22
import{UserProfileLayout}from"./UserProfileLayout";
33
import{getUser}from"../../redux/selectors/usersSelectors";
44
import{trans}from"../../i18n";
55
import{USER_PROFILE_URL}from"constants/routesURL";
6+
import{useEffect}from"react";
7+
import{fetchApiKeysAction}from"redux/reduxActions/userActions";
68

79
exportfunctionUserProfileView(){
810

911
constuser=useSelector(getUser);
12+
constdispatch=useDispatch();
13+
14+
useEffect(()=>{
15+
if(!user.currentOrgId)return;
16+
17+
dispatch(fetchApiKeysAction());
18+
},[]);
1019

1120
if(!user.currentOrgId){
1221
returnnull;
1322
}
1423

24+
1525
return<UserProfileLayoutbreadcrumb={[{text:trans("home.profile"),path:USER_PROFILE_URL}]}/>;
1626

1727
};
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import{useEffect,useMemo,useState}from"react";
2+
import{
3+
messageInstance,
4+
CustomSelect,
5+
CloseEyeIcon,
6+
CustomModal,
7+
UnderlineCss,
8+
}from"lowcoder-design";
9+
import{trans}from"i18n";
10+
import{defaultasForm}from"antd/es/form";
11+
import{defaultasInput}from"antd/es/input";
12+
import{validateResponse}from"api/apiUtils";
13+
import_from"lodash";
14+
import{styled}from"styled-components";
15+
importUserApi,{ApiKeyPayload}from"api/userApi";
16+
17+
constCustomModalStyled=styled(CustomModal)`
18+
button {
19+
margin-top: 20px;
20+
}
21+
`;
22+
23+
constFormStyled=styled(Form)`
24+
.ant-form-item-control-input-content > input,
25+
.ant-input-password {
26+
&:hover {
27+
border-color: #8b8fa3;
28+
}
29+
30+
&:focus,
31+
&.ant-input-affix-wrapper-focused {
32+
border-color: #3377ff;
33+
}
34+
}
35+
36+
.ant-form-item-label > label {
37+
font-size: 13px;
38+
line-height: 19px;
39+
.has-tip {
40+
${UnderlineCss};
41+
}
42+
}
43+
44+
.ant-input-password-icon.anticon {
45+
color: #8b8fa3;
46+
47+
&:hover {
48+
color: #222;
49+
}
50+
}
51+
52+
&.ant-form-vertical .ant-form-item-label {
53+
padding-bottom: 4px;
54+
}
55+
56+
.ant-form-item-explain-error {
57+
font-size: 12px;
58+
color: #f73131;
59+
line-height: 20px;
60+
}
61+
62+
.ant-form-item-label
63+
> label.ant-form-item-required:not(.ant-form-item-required-mark-optional)::before {
64+
color: #f73131;
65+
}
66+
67+
.ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless).ant-input,
68+
.ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless).ant-input:hover {
69+
border-color: #f73131;
70+
}
71+
72+
.register {
73+
margin: -4px 0 20px 0;
74+
}
75+
76+
.ant-input-prefix {
77+
margin-right: 8px;
78+
svg {
79+
path,
80+
rect:nth-of-type(1) {
81+
stroke: #8b8fa3;
82+
}
83+
rect:nth-of-type(2) {
84+
fill: #8b8fa3;
85+
}
86+
}
87+
}
88+
`;
89+
90+
typeCreateApiKeyModalProps={
91+
modalVisible:boolean;
92+
closeModal:()=>void;
93+
onConfigCreate:()=>void;
94+
};
95+
96+
functionCreateApiKeyModal(props:CreateApiKeyModalProps){
97+
const{
98+
modalVisible,
99+
closeModal,
100+
onConfigCreate
101+
}=props;
102+
const[form]=Form.useForm();
103+
const[saveLoading,setSaveLoading]=useState(false);
104+
105+
consthandleOk=()=>{
106+
form.validateFields().then(values=>{
107+
// console.log(values)
108+
createApiKey(values)
109+
})
110+
}
111+
functioncreateApiKey(values:ApiKeyPayload){
112+
setSaveLoading(true);
113+
114+
UserApi.createApiKey(values)
115+
.then((resp)=>{
116+
if(validateResponse(resp)){
117+
messageInstance.success(trans("idSource.saveSuccess"));
118+
}
119+
})
120+
.catch((e)=>messageInstance.error(e.message))
121+
.finally(()=>{
122+
setSaveLoading(false);
123+
onConfigCreate();
124+
});
125+
}
126+
127+
functionhandleCancel(){
128+
closeModal();
129+
form.resetFields();
130+
}
131+
132+
return(
133+
<CustomModalStyled
134+
width="500px"
135+
title={"Create API Key"}
136+
open={modalVisible}
137+
okText={"Save"}
138+
okButtonProps={{
139+
loading:saveLoading
140+
}}
141+
onOk={handleOk}
142+
onCancel={handleCancel}
143+
destroyOnClose
144+
afterClose={()=>form.resetFields()}
145+
>
146+
<FormStyled
147+
form={form}
148+
name="basic"
149+
layout="vertical"
150+
style={{maxWidth:440}}
151+
autoComplete="off"
152+
>
153+
<Form.Item
154+
name="name"
155+
label="Name"
156+
rules={[{required:true}]}
157+
>
158+
<Input
159+
placeholder={trans("idSource.formPlaceholder",{
160+
label:'Name'
161+
})}
162+
/>
163+
</Form.Item>
164+
<Form.Item
165+
name="description"
166+
label="Description"
167+
>
168+
<Input
169+
placeholder={trans("idSource.formPlaceholder",{
170+
label:'Description'
171+
})}
172+
/>
173+
</Form.Item>
174+
</FormStyled>
175+
</CustomModalStyled>
176+
);
177+
}
178+
179+
exportdefaultCreateApiKeyModal;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp