@greatapps/common 1.1.24 → 1.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/account/ConfigurationsMyAccountModal.mjs +121 -0
- package/dist/components/account/ConfigurationsMyAccountModal.mjs.map +1 -0
- package/dist/components/account/ConfirmGlobalPreferencesModal.mjs +45 -0
- package/dist/components/account/ConfirmGlobalPreferencesModal.mjs.map +1 -0
- package/dist/components/account/DisableTwoFactorAuthModal.mjs +67 -0
- package/dist/components/account/DisableTwoFactorAuthModal.mjs.map +1 -0
- package/dist/components/account/TwoFactorAuthModal.mjs +190 -0
- package/dist/components/account/TwoFactorAuthModal.mjs.map +1 -0
- package/dist/components/account/constants.mjs +32 -0
- package/dist/components/account/constants.mjs.map +1 -0
- package/dist/components/account/hooks/useChangeEmailForm.mjs +53 -0
- package/dist/components/account/hooks/useChangeEmailForm.mjs.map +1 -0
- package/dist/components/account/hooks/useChangePasswordForm.mjs +47 -0
- package/dist/components/account/hooks/useChangePasswordForm.mjs.map +1 -0
- package/dist/components/account/hooks/useChangePhoneForm.mjs +64 -0
- package/dist/components/account/hooks/useChangePhoneForm.mjs.map +1 -0
- package/dist/components/account/hooks/useConfigurationsModal.mjs +33 -0
- package/dist/components/account/hooks/useConfigurationsModal.mjs.map +1 -0
- package/dist/components/account/hooks/useDisableTwoFactorAuthForm.mjs +54 -0
- package/dist/components/account/hooks/useDisableTwoFactorAuthForm.mjs.map +1 -0
- package/dist/components/account/hooks/useOtpVerification.mjs +94 -0
- package/dist/components/account/hooks/useOtpVerification.mjs.map +1 -0
- package/dist/components/account/hooks/useTwoFactorAuthForm.mjs +88 -0
- package/dist/components/account/hooks/useTwoFactorAuthForm.mjs.map +1 -0
- package/dist/components/account/sections/ChangeEmailModal.mjs +180 -0
- package/dist/components/account/sections/ChangeEmailModal.mjs.map +1 -0
- package/dist/components/account/sections/ChangePasswordSection.mjs +64 -0
- package/dist/components/account/sections/ChangePasswordSection.mjs.map +1 -0
- package/dist/components/account/sections/ChangePhoneModal.mjs +182 -0
- package/dist/components/account/sections/ChangePhoneModal.mjs.map +1 -0
- package/dist/components/account/sections/MyProfileSection.mjs +312 -0
- package/dist/components/account/sections/MyProfileSection.mjs.map +1 -0
- package/dist/components/account/sections/PreferencesSection.mjs +246 -0
- package/dist/components/account/sections/PreferencesSection.mjs.map +1 -0
- package/dist/components/account/sections/SecuritySection.mjs +147 -0
- package/dist/components/account/sections/SecuritySection.mjs.map +1 -0
- package/dist/components/ui/form/ComboboxField.mjs +130 -0
- package/dist/components/ui/form/ComboboxField.mjs.map +1 -0
- package/dist/components/ui/form/FormField.mjs +103 -0
- package/dist/components/ui/form/FormField.mjs.map +1 -0
- package/dist/components/ui/form/PhoneInput.mjs +78 -0
- package/dist/components/ui/form/PhoneInput.mjs.map +1 -0
- package/dist/components/ui/form/SelectField.mjs +80 -0
- package/dist/components/ui/form/SelectField.mjs.map +1 -0
- package/dist/components/ui/form/SwitchOptionFieldWithIcon.mjs +40 -0
- package/dist/components/ui/form/SwitchOptionFieldWithIcon.mjs.map +1 -0
- package/dist/components/ui/form/TextAreaField.mjs +56 -0
- package/dist/components/ui/form/TextAreaField.mjs.map +1 -0
- package/dist/enums/AccountSectionType.mjs +11 -0
- package/dist/enums/AccountSectionType.mjs.map +1 -0
- package/dist/hooks/useCountdownTimer.mjs +29 -0
- package/dist/hooks/useCountdownTimer.mjs.map +1 -0
- package/dist/hooks/useIsMobile.mjs +18 -0
- package/dist/hooks/useIsMobile.mjs.map +1 -0
- package/dist/hooks/usePasswordVisibility.mjs +13 -0
- package/dist/hooks/usePasswordVisibility.mjs.map +1 -0
- package/dist/index.mjs +80 -0
- package/dist/index.mjs.map +1 -1
- package/dist/modules/accounts/actions/account-management.action.mjs +163 -0
- package/dist/modules/accounts/actions/account-management.action.mjs.map +1 -0
- package/dist/modules/accounts/hooks/useAccountManagement.mjs +64 -0
- package/dist/modules/accounts/hooks/useAccountManagement.mjs.map +1 -0
- package/dist/modules/accounts/services/account.service.mjs +162 -25
- package/dist/modules/accounts/services/account.service.mjs.map +1 -1
- package/dist/modules/accounts/services/two-factor.service.mjs +77 -0
- package/dist/modules/accounts/services/two-factor.service.mjs.map +1 -0
- package/dist/modules/auth/services/auth.service.mjs +7 -4
- package/dist/modules/auth/services/auth.service.mjs.map +1 -1
- package/dist/server.mjs +36 -0
- package/dist/server.mjs.map +1 -1
- package/dist/utils/browser/clipboard.mjs +36 -0
- package/dist/utils/browser/clipboard.mjs.map +1 -0
- package/dist/utils/file/index.mjs +9 -0
- package/dist/utils/file/index.mjs.map +1 -0
- package/dist/utils/format/masks.mjs +56 -0
- package/dist/utils/format/masks.mjs.map +1 -0
- package/dist/utils/intl/locales.mjs +12 -0
- package/dist/utils/intl/locales.mjs.map +1 -0
- package/dist/utils/intl/timezones.mjs +41 -0
- package/dist/utils/intl/timezones.mjs.map +1 -0
- package/dist/utils/validators/account.mjs +26 -0
- package/dist/utils/validators/account.mjs.map +1 -0
- package/dist/utils/validators/common.mjs +47 -0
- package/dist/utils/validators/common.mjs.map +1 -0
- package/package.json +5 -1
- package/src/components/account/ConfigurationsMyAccountModal.tsx +147 -0
- package/src/components/account/ConfirmGlobalPreferencesModal.tsx +66 -0
- package/src/components/account/DisableTwoFactorAuthModal.tsx +86 -0
- package/src/components/account/TwoFactorAuthModal.tsx +221 -0
- package/src/components/account/constants.ts +29 -0
- package/src/components/account/hooks/useChangeEmailForm.tsx +56 -0
- package/src/components/account/hooks/useChangePasswordForm.tsx +59 -0
- package/src/components/account/hooks/useChangePhoneForm.tsx +69 -0
- package/src/components/account/hooks/useConfigurationsModal.ts +40 -0
- package/src/components/account/hooks/useDisableTwoFactorAuthForm.tsx +69 -0
- package/src/components/account/hooks/useOtpVerification.tsx +116 -0
- package/src/components/account/hooks/useTwoFactorAuthForm.tsx +114 -0
- package/src/components/account/sections/ChangeEmailModal.tsx +224 -0
- package/src/components/account/sections/ChangePasswordSection.tsx +64 -0
- package/src/components/account/sections/ChangePhoneModal.tsx +229 -0
- package/src/components/account/sections/MyProfileSection.tsx +337 -0
- package/src/components/account/sections/PreferencesSection.tsx +277 -0
- package/src/components/account/sections/SecuritySection.tsx +160 -0
- package/src/components/ui/form/ComboboxField.tsx +154 -0
- package/src/components/ui/form/FormField.tsx +130 -0
- package/src/components/ui/form/PhoneInput.tsx +98 -0
- package/src/components/ui/form/SelectField.tsx +118 -0
- package/src/components/ui/form/SwitchOptionFieldWithIcon.tsx +47 -0
- package/src/components/ui/form/TextAreaField.tsx +70 -0
- package/src/enums/AccountSectionType.ts +6 -0
- package/src/hooks/useCountdownTimer.ts +41 -0
- package/src/hooks/useIsMobile.ts +19 -0
- package/src/hooks/usePasswordVisibility.ts +13 -0
- package/src/index.ts +70 -0
- package/src/modules/accounts/actions/account-management.action.ts +197 -0
- package/src/modules/accounts/hooks/useAccountManagement.ts +79 -0
- package/src/modules/accounts/services/account.service.ts +229 -50
- package/src/modules/accounts/services/two-factor.service.ts +98 -0
- package/src/modules/accounts/types.ts +157 -0
- package/src/modules/auth/services/auth.service.ts +9 -5
- package/src/server.ts +21 -0
- package/src/utils/browser/clipboard.ts +34 -0
- package/src/utils/file/index.ts +5 -0
- package/src/utils/format/masks.ts +52 -0
- package/src/utils/intl/locales.ts +15 -0
- package/src/utils/intl/timezones.ts +53 -0
- package/src/utils/validators/account.ts +36 -0
- package/src/utils/validators/common.ts +43 -0
|
@@ -1,57 +1,236 @@
|
|
|
1
1
|
import 'server-only';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
import { api } from '../../../infra/api/client';
|
|
4
|
+
import { ApiError } from '../../../infra/api/types';
|
|
5
|
+
import { buildQueryParams } from '../../../infra/utils/params';
|
|
6
|
+
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
7
|
+
import { fileToBase64 } from '../../../utils/file/index';
|
|
8
|
+
import type {
|
|
9
|
+
Account,
|
|
10
|
+
AccountUsersPaginationParams,
|
|
11
|
+
ChangePasswordRequest,
|
|
12
|
+
ChangePasswordResponse,
|
|
13
|
+
ChangeUserPhotoResponse,
|
|
14
|
+
CreateAccountUserRequest,
|
|
15
|
+
CreateAccountUserResponse,
|
|
16
|
+
DeleteAccountResponse,
|
|
17
|
+
DeleteAccountUserResponse,
|
|
18
|
+
GetAccountDataResponse,
|
|
19
|
+
ListAccountUsersResponse,
|
|
20
|
+
UpdateAccountRequest,
|
|
21
|
+
UpdateAccountResponse,
|
|
22
|
+
UpdateAccountUserRequest,
|
|
23
|
+
UpdateAccountUserResponse,
|
|
24
|
+
} from '../types';
|
|
25
|
+
|
|
12
26
|
class AccountService {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
27
|
+
async findCurrentAccount(): Promise<Account> {
|
|
28
|
+
const { id_account } = await getUserContext();
|
|
29
|
+
const response = await api.apps.get<GetAccountDataResponse>(`/accounts/${id_account}`);
|
|
30
|
+
|
|
31
|
+
if (response.status === 0 || !response.data?.[0]) {
|
|
32
|
+
throw new ApiError(
|
|
33
|
+
response.message || 'Erro ao buscar dados da conta',
|
|
34
|
+
'GET_ACCOUNT_FAILED',
|
|
35
|
+
400
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return response.data[0];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async listAccountUsers(params?: Partial<AccountUsersPaginationParams>) {
|
|
43
|
+
const { id_account } = await getUserContext();
|
|
44
|
+
const queryParams = buildQueryParams(params);
|
|
45
|
+
const endpoint = `/accounts/${id_account}/users?${queryParams}`;
|
|
46
|
+
const response = await api.apps.get<ListAccountUsersResponse>(endpoint);
|
|
47
|
+
|
|
48
|
+
if (response.status === 0) {
|
|
49
|
+
throw new ApiError(
|
|
50
|
+
response.message || 'Erro ao listar usuários',
|
|
51
|
+
'LIST_ACCOUNT_USERS_FAILED',
|
|
52
|
+
400
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
users: response.data || [],
|
|
58
|
+
total: response.total || 0,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async changeUserPhoto(userId: number, photo: File): Promise<ChangeUserPhotoResponse> {
|
|
63
|
+
const { id_account } = await getUserContext();
|
|
64
|
+
const base64 = await fileToBase64(photo);
|
|
65
|
+
const response = await api.apps.put<ChangeUserPhotoResponse>(
|
|
66
|
+
`/accounts/${id_account}/users/${userId}/file/photo`,
|
|
67
|
+
{ file: base64 }
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (response.status === 0) {
|
|
71
|
+
throw new ApiError(
|
|
72
|
+
response.message || 'Erro ao mudar foto do usuário',
|
|
73
|
+
'CHANGE_USER_PHOTO_FAILED',
|
|
74
|
+
400
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return response;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async deleteAccountUser(userId: number): Promise<DeleteAccountUserResponse> {
|
|
82
|
+
const { id_account } = await getUserContext();
|
|
83
|
+
const response = await api.apps.delete<DeleteAccountUserResponse>(
|
|
84
|
+
`/accounts/${id_account}/users/${userId}`
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (response.status === 0) {
|
|
88
|
+
throw new ApiError(response.message || 'Erro ao deletar usuário', 'DELETE_USER_FAILED', 400);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return response;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async deleteAccount(): Promise<DeleteAccountResponse> {
|
|
95
|
+
const { id_account } = await getUserContext();
|
|
96
|
+
const response = await api.apps.delete<DeleteAccountResponse>(`/accounts/${id_account}`);
|
|
97
|
+
|
|
98
|
+
if (response.status === 0) {
|
|
99
|
+
throw new ApiError(response.message || 'Erro ao deletar conta', 'DELETE_ACCOUNT_FAILED', 400);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return response;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async createAccountUser(user: CreateAccountUserRequest): Promise<CreateAccountUserResponse> {
|
|
106
|
+
const { id_account } = await getUserContext();
|
|
107
|
+
const response = await api.apps.post<CreateAccountUserResponse>(
|
|
108
|
+
`/accounts/${id_account}/users`,
|
|
109
|
+
{
|
|
110
|
+
...user,
|
|
111
|
+
photo: user.photo ? await fileToBase64(user.photo) : null,
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (response.status === 0) {
|
|
116
|
+
throw new ApiError(response.message || 'Erro ao criar usuário', 'CREATE_USER_FAILED', 400);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return response;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async resolvePhotoField(user: UpdateAccountUserRequest): Promise<{ photo?: string | null }> {
|
|
123
|
+
if (user.photo === undefined) return {};
|
|
124
|
+
const photo =
|
|
125
|
+
user.photo != null && typeof user.photo !== 'string'
|
|
126
|
+
? await fileToBase64(user.photo)
|
|
127
|
+
: user.photo;
|
|
128
|
+
return { photo };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async updateAccountUser(user: UpdateAccountUserRequest): Promise<UpdateAccountUserResponse> {
|
|
132
|
+
const { id_account, id_user } = await getUserContext();
|
|
133
|
+
const response = await api.apps.put<UpdateAccountUserResponse>(
|
|
134
|
+
`/accounts/${id_account}/users/${id_user}`,
|
|
135
|
+
{ ...user, ...(await this.resolvePhotoField(user)) }
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
if (response.status === 0) {
|
|
139
|
+
throw new ApiError(
|
|
140
|
+
response.message || 'Erro ao atualizar usuário',
|
|
141
|
+
'UPDATE_USER_FAILED',
|
|
142
|
+
400
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return response;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async updateAccountUserById(
|
|
150
|
+
userId: number,
|
|
151
|
+
user: UpdateAccountUserRequest
|
|
152
|
+
): Promise<UpdateAccountUserResponse> {
|
|
153
|
+
const { id_account } = await getUserContext();
|
|
154
|
+
const response = await api.apps.put<UpdateAccountUserResponse>(
|
|
155
|
+
`/accounts/${id_account}/users/${userId}`,
|
|
156
|
+
{ ...user, ...(await this.resolvePhotoField(user)) }
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (response.status === 0) {
|
|
160
|
+
throw new ApiError(
|
|
161
|
+
response.message || 'Erro ao atualizar usuário',
|
|
162
|
+
'UPDATE_USER_FAILED',
|
|
163
|
+
400
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return response;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async updateAccount(data: UpdateAccountRequest): Promise<UpdateAccountResponse> {
|
|
171
|
+
const { id_account } = await getUserContext();
|
|
172
|
+
const response = await api.apps.put<UpdateAccountResponse>(`/accounts/${id_account}`, data);
|
|
173
|
+
|
|
174
|
+
if (response.status === 0) {
|
|
175
|
+
throw new ApiError(
|
|
176
|
+
response.message || 'Erro ao atualizar conta',
|
|
177
|
+
'UPDATE_ACCOUNT_FAILED',
|
|
178
|
+
400
|
|
179
|
+
);
|
|
54
180
|
}
|
|
181
|
+
|
|
182
|
+
return response;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async requestContactReset(type: 'email' | 'phone', newContact: string): Promise<void> {
|
|
186
|
+
const { id_account, id_user } = await getUserContext();
|
|
187
|
+
const response = await api.apps.post<{ status: number; message: string }>(
|
|
188
|
+
`/accounts/${id_account}/users/${id_user}/resetContact/request`,
|
|
189
|
+
{ type, newContact }
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
if (response.status === 0) {
|
|
193
|
+
throw new ApiError(
|
|
194
|
+
response.message || 'Erro ao solicitar alteração',
|
|
195
|
+
'REQUEST_CONTACT_RESET_FAILED',
|
|
196
|
+
400
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async confirmContactReset(type: 'email' | 'phone', token: string, newContact: string): Promise<void> {
|
|
202
|
+
const { id_account, id_user } = await getUserContext();
|
|
203
|
+
const response = await api.apps.post<{ status: number; message: string }>(
|
|
204
|
+
`/accounts/${id_account}/users/${id_user}/resetContact/confirm`,
|
|
205
|
+
{ type, token, newContact }
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
if (response.status === 0) {
|
|
209
|
+
throw new ApiError(
|
|
210
|
+
response.message || 'Código inválido ou expirado',
|
|
211
|
+
'CONFIRM_CONTACT_RESET_FAILED',
|
|
212
|
+
400
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async changePassword(data: ChangePasswordRequest): Promise<ChangePasswordResponse> {
|
|
218
|
+
const { id_account, id_user } = await getUserContext();
|
|
219
|
+
const response = await api.apps.put<ChangePasswordResponse>(
|
|
220
|
+
`/accounts/${id_account}/users/${id_user}/action/changePassword`,
|
|
221
|
+
data
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
if (response.status === 0) {
|
|
225
|
+
throw new ApiError(
|
|
226
|
+
response.message || 'Erro ao alterar senha',
|
|
227
|
+
'CHANGE_PASSWORD_FAILED',
|
|
228
|
+
400
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return response;
|
|
233
|
+
}
|
|
55
234
|
}
|
|
56
235
|
|
|
57
236
|
export const accountService = new AccountService();
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import { ApiError } from '../../../infra/api/types';
|
|
4
|
+
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
5
|
+
import { findWhitelabel } from '../../whitelabel/actions/find-whitelabel.action';
|
|
6
|
+
import type { TwoFactorActionResponse, TwoFactorGenerateResponse } from '../types';
|
|
7
|
+
|
|
8
|
+
const TWO_FA_BASE_URL = process.env.R2_2FA_URL || 'https://r2-2fa.greatapps.dev.br';
|
|
9
|
+
|
|
10
|
+
class TwoFactorService {
|
|
11
|
+
private async request<T>(path: string, body?: object): Promise<T> {
|
|
12
|
+
const { id, token } = await findWhitelabel();
|
|
13
|
+
const { id_account, id_user } = await getUserContext();
|
|
14
|
+
|
|
15
|
+
const url = `${TWO_FA_BASE_URL}/v1/${id}/account/${id_account}/users/${id_user}/2fa/${path}`;
|
|
16
|
+
|
|
17
|
+
const response = await fetch(url, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
authorization: token,
|
|
22
|
+
},
|
|
23
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const errorData = await response.json().catch(() => ({})) as Record<string, unknown>;
|
|
28
|
+
throw new ApiError(
|
|
29
|
+
(errorData.message as string) || `HTTP error ${response.status}`,
|
|
30
|
+
'HTTP_ERROR',
|
|
31
|
+
response.status
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return response.json() as Promise<T>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async generate(): Promise<TwoFactorGenerateResponse> {
|
|
39
|
+
const response = await this.request<TwoFactorGenerateResponse>('generate');
|
|
40
|
+
|
|
41
|
+
if (response.status === 0) {
|
|
42
|
+
throw new ApiError(response.message || 'Erro ao gerar QR Code', 'GENERATE_2FA_FAILED', 400);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return response;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async confirm(code: string): Promise<TwoFactorActionResponse> {
|
|
49
|
+
const response = await this.request<TwoFactorActionResponse>('confirm', { code });
|
|
50
|
+
|
|
51
|
+
if (response.status === 0) {
|
|
52
|
+
throw new ApiError(response.message || 'Código inválido', 'CONFIRM_2FA_FAILED', 400);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return response;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async verify(code: string): Promise<TwoFactorActionResponse> {
|
|
59
|
+
const response = await this.request<TwoFactorActionResponse>('verify', { code });
|
|
60
|
+
|
|
61
|
+
if (response.status === 0) {
|
|
62
|
+
throw new ApiError(response.message || 'Código inválido', 'VERIFY_2FA_FAILED', 400);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return response;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async disable(): Promise<TwoFactorActionResponse> {
|
|
69
|
+
const { id, token } = await findWhitelabel();
|
|
70
|
+
const { id_account, id_user } = await getUserContext();
|
|
71
|
+
|
|
72
|
+
const url = `${TWO_FA_BASE_URL}/v1/${id}/account/${id_account}/users/${id_user}/2fa`;
|
|
73
|
+
|
|
74
|
+
const response = await fetch(url, {
|
|
75
|
+
method: 'DELETE',
|
|
76
|
+
headers: { authorization: token },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
const errorData = await response.json().catch(() => ({})) as Record<string, unknown>;
|
|
81
|
+
throw new ApiError(
|
|
82
|
+
(errorData.message as string) || `HTTP error ${response.status}`,
|
|
83
|
+
'HTTP_ERROR',
|
|
84
|
+
response.status
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const data = await response.json() as TwoFactorActionResponse;
|
|
89
|
+
|
|
90
|
+
if (data.status === 0) {
|
|
91
|
+
throw new ApiError(data.message || 'Erro ao desabilitar 2FA', 'DISABLE_2FA_FAILED', 400);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return data;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const twoFactorService = new TwoFactorService();
|
|
@@ -53,4 +53,161 @@ export interface GetAccountDataResponse {
|
|
|
53
53
|
message: string;
|
|
54
54
|
data: Account[];
|
|
55
55
|
total: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Account User Management
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
export interface AccountUsersActionResult {
|
|
63
|
+
success: boolean;
|
|
64
|
+
data?: User[];
|
|
65
|
+
total?: number;
|
|
66
|
+
error?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CreateAccountUserRequest {
|
|
70
|
+
name: string;
|
|
71
|
+
email: string;
|
|
72
|
+
password?: string;
|
|
73
|
+
profile: UserProfile;
|
|
74
|
+
language: string;
|
|
75
|
+
id_api?: number;
|
|
76
|
+
last_name: string;
|
|
77
|
+
ddi: string;
|
|
78
|
+
phone: string;
|
|
79
|
+
rg?: string;
|
|
80
|
+
cpf?: string;
|
|
81
|
+
gender: number;
|
|
82
|
+
receive_sms?: boolean;
|
|
83
|
+
receive_email?: boolean;
|
|
84
|
+
photo?: File | null;
|
|
85
|
+
projects?: number[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface CreateAccountUserResponse {
|
|
89
|
+
status: 0 | 1;
|
|
90
|
+
message: string;
|
|
91
|
+
data: User[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface UpdateAccountUserRequest {
|
|
95
|
+
name?: string;
|
|
96
|
+
email?: string;
|
|
97
|
+
password?: string;
|
|
98
|
+
profile?: UserProfile;
|
|
99
|
+
language?: string;
|
|
100
|
+
id_api?: number;
|
|
101
|
+
last_name?: string;
|
|
102
|
+
ddi?: string;
|
|
103
|
+
phone?: string;
|
|
104
|
+
rg?: string;
|
|
105
|
+
cpf?: string;
|
|
106
|
+
gender?: number;
|
|
107
|
+
receive_sms?: boolean;
|
|
108
|
+
receive_email?: boolean;
|
|
109
|
+
photo?: File | string | null;
|
|
110
|
+
projects?: number[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface UpdateAccountUserResponse {
|
|
114
|
+
status: 0 | 1;
|
|
115
|
+
message: string;
|
|
116
|
+
data: User[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface ChangeUserPhotoResponse {
|
|
120
|
+
status: 0 | 1;
|
|
121
|
+
message?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface DeleteAccountUserResponse {
|
|
125
|
+
status: 0 | 1;
|
|
126
|
+
message?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface DeleteAccountResponse {
|
|
130
|
+
status: 0 | 1;
|
|
131
|
+
message?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface DeleteAccountActionResult {
|
|
135
|
+
success: boolean;
|
|
136
|
+
error?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface DeleteUserActionResult {
|
|
140
|
+
success: boolean;
|
|
141
|
+
error?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface UpdateAccountRequest {
|
|
145
|
+
currency?: string;
|
|
146
|
+
timezone?: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface UpdateAccountResponse {
|
|
150
|
+
status: 0 | 1;
|
|
151
|
+
message: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface UpdateAccountActionResult {
|
|
155
|
+
success: boolean;
|
|
156
|
+
error?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface UpdateUserActionResult {
|
|
160
|
+
success: boolean;
|
|
161
|
+
data?: UpdateAccountUserResponse;
|
|
162
|
+
error?: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface ChangePasswordRequest {
|
|
166
|
+
currentPassword: string;
|
|
167
|
+
newPassword: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface ChangePasswordResponse {
|
|
171
|
+
status: 0 | 1;
|
|
172
|
+
message: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface ChangePasswordActionResult {
|
|
176
|
+
success: boolean;
|
|
177
|
+
error?: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface ContactResetResult {
|
|
181
|
+
success: boolean;
|
|
182
|
+
error?: string;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ============================================================================
|
|
186
|
+
// Two-Factor Authentication
|
|
187
|
+
// ============================================================================
|
|
188
|
+
|
|
189
|
+
export interface TwoFactorGenerateResponse {
|
|
190
|
+
status: 0 | 1;
|
|
191
|
+
message: string;
|
|
192
|
+
data: {
|
|
193
|
+
qrcode: string;
|
|
194
|
+
backup_codes: string[];
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface TwoFactorActionResponse {
|
|
199
|
+
status: 0 | 1;
|
|
200
|
+
message: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface TwoFactorGenerateResult {
|
|
204
|
+
success: boolean;
|
|
205
|
+
qrcode?: string;
|
|
206
|
+
backupCodes?: string[];
|
|
207
|
+
error?: string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface TwoFactorActionResult {
|
|
211
|
+
success: boolean;
|
|
212
|
+
error?: string;
|
|
56
213
|
}
|
|
@@ -144,10 +144,12 @@ class AuthService {
|
|
|
144
144
|
async logout(clientInfo: ClientInfo): Promise<LogoutResponse> {
|
|
145
145
|
const cookie = await getAuthCookie();
|
|
146
146
|
|
|
147
|
+
|
|
147
148
|
if (!cookie) {
|
|
148
149
|
throw new ApiError('Usuário não autenticado', 'NOT_AUTHENTICATED', 401);
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
|
|
151
153
|
const payload: LogoutRequest = {
|
|
152
154
|
location: clientInfo.location,
|
|
153
155
|
ip: clientInfo.ip,
|
|
@@ -156,14 +158,16 @@ class AuthService {
|
|
|
156
158
|
cookie,
|
|
157
159
|
};
|
|
158
160
|
|
|
159
|
-
|
|
161
|
+
try {
|
|
162
|
+
const response = await apiClient.post<LogoutApiResponse>('/auth/logout', payload);
|
|
160
163
|
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
if (response.status === 0) {
|
|
165
|
+
throw new ApiError(response.message || 'Erro ao realizar logout', 'LOGOUT_FAILED', 400);
|
|
166
|
+
}
|
|
167
|
+
} finally {
|
|
168
|
+
await removeAuthCookie();
|
|
163
169
|
}
|
|
164
170
|
|
|
165
|
-
await removeAuthCookie();
|
|
166
|
-
|
|
167
171
|
return {
|
|
168
172
|
success: true,
|
|
169
173
|
};
|
package/src/server.ts
CHANGED
|
@@ -15,6 +15,27 @@ export { validateSessionAction } from './modules/auth/actions/validate-session.a
|
|
|
15
15
|
export { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';
|
|
16
16
|
export { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';
|
|
17
17
|
|
|
18
|
+
// Account Services & Actions (server-only)
|
|
19
|
+
export { accountService } from './modules/accounts/services/account.service';
|
|
20
|
+
export { twoFactorService } from './modules/accounts/services/two-factor.service';
|
|
21
|
+
export {
|
|
22
|
+
listAccountUsersAction,
|
|
23
|
+
updateUserAction,
|
|
24
|
+
updateAccountAction,
|
|
25
|
+
updateAccountUserByIdAction,
|
|
26
|
+
deleteAccountUserAction,
|
|
27
|
+
deleteAccountAction,
|
|
28
|
+
createAccountUserAction,
|
|
29
|
+
changePasswordAction,
|
|
30
|
+
requestEmailChangeAction,
|
|
31
|
+
confirmEmailChangeAction,
|
|
32
|
+
requestPhoneChangeAction,
|
|
33
|
+
confirmPhoneChangeAction,
|
|
34
|
+
generateTwoFactorAction,
|
|
35
|
+
confirmTwoFactorAction,
|
|
36
|
+
disableTwoFactorAction,
|
|
37
|
+
} from './modules/accounts/actions/account-management.action';
|
|
38
|
+
|
|
18
39
|
// Server Utils
|
|
19
40
|
export { getClientInfoFromRequest } from './infra/utils/client-info';
|
|
20
41
|
export { getUserContext } from './modules/auth/utils/get-user-context';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const copyToClipboard = async (text: string): Promise<boolean> => {
|
|
2
|
+
try {
|
|
3
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
4
|
+
await navigator.clipboard.writeText(text);
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const textArea = document.createElement('textarea');
|
|
9
|
+
textArea.value = text;
|
|
10
|
+
textArea.style.position = 'fixed';
|
|
11
|
+
textArea.style.left = '-9999px';
|
|
12
|
+
textArea.style.top = '-9999px';
|
|
13
|
+
document.body.appendChild(textArea);
|
|
14
|
+
textArea.focus();
|
|
15
|
+
textArea.select();
|
|
16
|
+
|
|
17
|
+
const success = document.execCommand('copy');
|
|
18
|
+
document.body.removeChild(textArea);
|
|
19
|
+
return success;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error('Erro ao copiar para clipboard:', error);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const readFromClipboard = async (): Promise<string | null> => {
|
|
27
|
+
try {
|
|
28
|
+
const text = await navigator.clipboard.readText();
|
|
29
|
+
return text;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Erro ao ler do clipboard:', error);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|