@mindline/sync 1.0.110 → 1.0.112
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/.vs/VSWorkspaceState.json +2 -3
- package/.vs/slnx.sqlite +0 -0
- package/.vs/sync.slnx/FileContentIndex/1ca8d456-29aa-416f-9787-e53a88d5d5ab.vsidx +0 -0
- package/.vs/sync.slnx/FileContentIndex/3741cf32-b4b7-4b99-9366-c24391f22a74.vsidx +0 -0
- package/.vs/sync.slnx/FileContentIndex/a8b9c3a0-52bd-4e8b-ad08-9046e741e403.vsidx +0 -0
- package/.vs/sync.slnx/FileContentIndex/{233f16f5-9502-4eee-892d-94508f320b43.vsidx → c8e19530-a150-45f1-a571-cd5fdea9925a.vsidx} +0 -0
- package/.vs/sync.slnx/FileContentIndex/ced1adfa-715d-4462-88ac-43cb0271386e.vsidx +0 -0
- package/.vs/sync.slnx/v18/.wsuo +0 -0
- package/.vs/sync.slnx/v18/DocumentLayout.backup.json +27 -12
- package/.vs/sync.slnx/v18/DocumentLayout.json +23 -8
- package/dist/src/api-client.d.ts +74 -0
- package/dist/src/index.d.ts +8 -10
- package/dist/sync.es.js +1021 -1073
- package/dist/sync.es.js.map +1 -1
- package/dist/sync.umd.js +50 -50
- package/dist/sync.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/api-client.ts +382 -0
- package/src/index.ts +52 -419
- package/.vs/sync.slnx/FileContentIndex/46af7fa0-1a65-4634-9faa-600623e92173.vsidx +0 -0
- package/.vs/sync.slnx/FileContentIndex/88b226f1-9afd-4cf3-bdb6-5db742bb7e8d.vsidx +0 -0
- package/.vs/sync.slnx/FileContentIndex/eb2f80ef-22e0-4cc0-a8fb-f7837f87af2d.vsidx +0 -0
- package/.vs/sync.slnx/FileContentIndex/f109c15d-d422-45e9-a5df-0b391ae0a643.vsidx +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Client for backend proxy endpoints
|
|
3
|
+
* This file contains functions that call backend controllers instead of directly calling
|
|
4
|
+
* Microsoft Graph API and Azure Management API from the frontend.
|
|
5
|
+
* This prevents token exposure in the browser.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ======================= Backend Response Types =======================
|
|
9
|
+
|
|
10
|
+
interface ApiResponse<T> {
|
|
11
|
+
data?: T;
|
|
12
|
+
error: string;
|
|
13
|
+
success: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Backend DTOs (match the C# DTOs)
|
|
17
|
+
interface GroupDto {
|
|
18
|
+
id: string;
|
|
19
|
+
displayName: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
mail?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface GroupsResponseDto {
|
|
25
|
+
groups: GroupDto[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface OAuth2PermissionGrantDto {
|
|
29
|
+
grants: string | null;
|
|
30
|
+
id: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ServicePrincipalDto {
|
|
34
|
+
servicePrincipalId: string;
|
|
35
|
+
displayName: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface UserDto {
|
|
39
|
+
id: string;
|
|
40
|
+
displayName: string;
|
|
41
|
+
mail?: string;
|
|
42
|
+
userPrincipalName?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface UsersResponseDto {
|
|
46
|
+
users: UserDto[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface CanListRootAssignmentsResponseDto {
|
|
50
|
+
canList: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ElevateAccessResponseDto {
|
|
54
|
+
success: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface ResourceDto {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
type: string;
|
|
61
|
+
location: string;
|
|
62
|
+
tags?: { [key: string]: string };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface ResourcesResponseDto {
|
|
66
|
+
resources: ResourceDto[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ======================= Frontend Types (for return values) =======================
|
|
70
|
+
// These match the types expected by index.ts callers
|
|
71
|
+
|
|
72
|
+
export interface Group {
|
|
73
|
+
id: string;
|
|
74
|
+
displayName: string;
|
|
75
|
+
description: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface Resource {
|
|
79
|
+
id: string;
|
|
80
|
+
name: string;
|
|
81
|
+
type: string;
|
|
82
|
+
location: string;
|
|
83
|
+
tags?: { [key: string]: string };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ======================= Microsoft Graph API Proxy Functions =======================
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Search for groups by display name
|
|
90
|
+
* Calls backend: GET /api/graphapi/groups
|
|
91
|
+
*/
|
|
92
|
+
export async function groupsGet(groupSearchString: string): Promise<{ groups: Group[], error: string }> {
|
|
93
|
+
try {
|
|
94
|
+
if (!groupSearchString) {
|
|
95
|
+
return { groups: [], error: '400: Search string is required' };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const url = `/api/graphapi/groups?search=${encodeURIComponent(groupSearchString)}`;
|
|
99
|
+
const response = await fetch(url, {
|
|
100
|
+
method: 'GET',
|
|
101
|
+
headers: {
|
|
102
|
+
'Content-Type': 'application/json',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
return { groups: [], error: `${response.status}: Failed to retrieve groups` };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const apiResponse: ApiResponse<GroupsResponseDto> = await response.json();
|
|
111
|
+
|
|
112
|
+
if (!apiResponse.success || !apiResponse.data) {
|
|
113
|
+
return { groups: [], error: apiResponse.error || 'Unknown error' };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Convert backend types to frontend types
|
|
117
|
+
const groups: Group[] = apiResponse.data.groups.map(g => ({
|
|
118
|
+
id: g.id,
|
|
119
|
+
displayName: g.displayName,
|
|
120
|
+
description: g.description || ''
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
return { groups, error: '' };
|
|
124
|
+
} catch (error: any) {
|
|
125
|
+
console.error('Error in groupsGet:', error);
|
|
126
|
+
return { groups: [], error: `Exception: ${error.message}` };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get OAuth2 permission grants for a service principal and principal
|
|
132
|
+
* Calls backend: GET /api/graphapi/oauth2-permission-grants
|
|
133
|
+
*/
|
|
134
|
+
export async function oauth2PermissionGrantsGet(
|
|
135
|
+
spid: string,
|
|
136
|
+
oid: string
|
|
137
|
+
): Promise<{ grants: string | null, id: string | null, error: string }> {
|
|
138
|
+
try {
|
|
139
|
+
if (!spid || !oid) {
|
|
140
|
+
return { grants: null, id: null, error: '400: Service Principal ID and Principal ID are required' };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const url = `/api/graphapi/oauth2-permission-grants?spid=${encodeURIComponent(spid)}&oid=${encodeURIComponent(oid)}`;
|
|
144
|
+
const response = await fetch(url, {
|
|
145
|
+
method: 'GET',
|
|
146
|
+
headers: {
|
|
147
|
+
'Content-Type': 'application/json',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
return { grants: null, id: null, error: `${response.status}: Failed to retrieve permission grants` };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const apiResponse: ApiResponse<OAuth2PermissionGrantDto> = await response.json();
|
|
156
|
+
|
|
157
|
+
if (!apiResponse.success || !apiResponse.data) {
|
|
158
|
+
return { grants: null, id: null, error: apiResponse.error || 'Unknown error' };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
grants: apiResponse.data.grants,
|
|
163
|
+
id: apiResponse.data.id,
|
|
164
|
+
error: ''
|
|
165
|
+
};
|
|
166
|
+
} catch (error: any) {
|
|
167
|
+
console.error('Error in oauth2PermissionGrantsGet:', error);
|
|
168
|
+
return { grants: null, id: null, error: `Exception: ${error.message}` };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Update OAuth2 permission grants
|
|
174
|
+
* Calls backend: PATCH /api/graphapi/oauth2-permission-grants/{id}
|
|
175
|
+
*/
|
|
176
|
+
export async function oauth2PermissionGrantsSet(id: string, scopes: string): Promise<boolean> {
|
|
177
|
+
try {
|
|
178
|
+
if (!id || !scopes) {
|
|
179
|
+
console.error('oauth2PermissionGrantsSet: ID and scopes are required');
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const url = `/api/graphapi/oauth2-permission-grants/${encodeURIComponent(id)}`;
|
|
184
|
+
const response = await fetch(url, {
|
|
185
|
+
method: 'PATCH',
|
|
186
|
+
headers: {
|
|
187
|
+
'Content-Type': 'application/json',
|
|
188
|
+
},
|
|
189
|
+
body: JSON.stringify({ scopes }),
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (!response.ok) {
|
|
193
|
+
console.error(`oauth2PermissionGrantsSet: PATCH failed with status ${response.status}`);
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const apiResponse: ApiResponse<boolean> = await response.json();
|
|
198
|
+
|
|
199
|
+
if (!apiResponse.success || !apiResponse.data) {
|
|
200
|
+
console.error(`oauth2PermissionGrantsSet: ${apiResponse.error}`);
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return apiResponse.data;
|
|
205
|
+
} catch (error: any) {
|
|
206
|
+
console.error('Error in oauth2PermissionGrantsSet:', error);
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get service principal by application ID
|
|
213
|
+
* Calls backend: GET /api/graphapi/service-principals
|
|
214
|
+
*/
|
|
215
|
+
export async function servicePrincipalGet(appid: string): Promise<{ spid: string, error: string }> {
|
|
216
|
+
try {
|
|
217
|
+
if (!appid) {
|
|
218
|
+
return { spid: '', error: '400: Application ID is required' };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const url = `/api/graphapi/service-principals?appId=${encodeURIComponent(appid)}`;
|
|
222
|
+
const response = await fetch(url, {
|
|
223
|
+
method: 'GET',
|
|
224
|
+
headers: {
|
|
225
|
+
'Content-Type': 'application/json',
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
return { spid: '', error: `${response.status}: Failed to retrieve service principal` };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const apiResponse: ApiResponse<ServicePrincipalDto> = await response.json();
|
|
234
|
+
|
|
235
|
+
if (!apiResponse.success || !apiResponse.data) {
|
|
236
|
+
return { spid: '', error: apiResponse.error || 'Unknown error' };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { spid: apiResponse.data.servicePrincipalId, error: '' };
|
|
240
|
+
} catch (error: any) {
|
|
241
|
+
console.error('Error in servicePrincipalGet:', error);
|
|
242
|
+
return { spid: '', error: `Exception: ${error.message}` };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Search for users. When no search string is provided, lists all users (matching original behavior).
|
|
248
|
+
* Calls backend: GET /api/graphapi/users
|
|
249
|
+
*/
|
|
250
|
+
export async function usersGet(userSearchString?: string): Promise<{ users: string[], error: string }> {
|
|
251
|
+
try {
|
|
252
|
+
const url = userSearchString
|
|
253
|
+
? `/api/graphapi/users?search=${encodeURIComponent(userSearchString)}`
|
|
254
|
+
: `/api/graphapi/users`;
|
|
255
|
+
const response = await fetch(url, {
|
|
256
|
+
method: 'GET',
|
|
257
|
+
headers: {
|
|
258
|
+
'Content-Type': 'application/json',
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (!response.ok) {
|
|
263
|
+
return { users: [], error: `${response.status}: Failed to retrieve users` };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const apiResponse: ApiResponse<UsersResponseDto> = await response.json();
|
|
267
|
+
|
|
268
|
+
if (!apiResponse.success || !apiResponse.data) {
|
|
269
|
+
return { users: [], error: apiResponse.error || 'Unknown error' };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Convert to array of mail addresses (matching original behavior that returned user.mail)
|
|
273
|
+
const users = apiResponse.data.users.map(u => u.mail || u.userPrincipalName || u.displayName);
|
|
274
|
+
|
|
275
|
+
return { users, error: '' };
|
|
276
|
+
} catch (error: any) {
|
|
277
|
+
console.error('Error in usersGet:', error);
|
|
278
|
+
return { users: [], error: `Exception: ${error.message}` };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ======================= Azure Management API Proxy Functions =======================
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Check if the current user can list root assignments
|
|
286
|
+
* Calls backend: GET /api/azuremanagement/can-list-root-assignments
|
|
287
|
+
*/
|
|
288
|
+
export async function canListRootAssignments(): Promise<boolean> {
|
|
289
|
+
try {
|
|
290
|
+
const url = '/api/azuremanagement/can-list-root-assignments';
|
|
291
|
+
const response = await fetch(url, {
|
|
292
|
+
method: 'GET',
|
|
293
|
+
headers: {
|
|
294
|
+
'Content-Type': 'application/json',
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (!response.ok) {
|
|
299
|
+
console.error(`canListRootAssignments: Request failed with status ${response.status}`);
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const apiResponse: ApiResponse<CanListRootAssignmentsResponseDto> = await response.json();
|
|
304
|
+
|
|
305
|
+
if (!apiResponse.success || !apiResponse.data) {
|
|
306
|
+
console.error(`canListRootAssignments: ${apiResponse.error}`);
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return apiResponse.data.canList;
|
|
311
|
+
} catch (error: any) {
|
|
312
|
+
console.error('Error in canListRootAssignments:', error);
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Elevate Global Admin to User Access Administrator role
|
|
319
|
+
* WARNING: This is a highly sensitive operation
|
|
320
|
+
* Calls backend: POST /api/azuremanagement/elevate-access
|
|
321
|
+
*/
|
|
322
|
+
export async function elevateGlobalAdminToUserAccessAdmin(): Promise<boolean> {
|
|
323
|
+
try {
|
|
324
|
+
const url = '/api/azuremanagement/elevate-access';
|
|
325
|
+
const response = await fetch(url, {
|
|
326
|
+
method: 'POST',
|
|
327
|
+
headers: {
|
|
328
|
+
'Content-Type': 'application/json',
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (!response.ok) {
|
|
333
|
+
console.error(`elevateGlobalAdminToUserAccessAdmin: Request failed with status ${response.status}`);
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const apiResponse: ApiResponse<ElevateAccessResponseDto> = await response.json();
|
|
338
|
+
|
|
339
|
+
if (!apiResponse.success || !apiResponse.data) {
|
|
340
|
+
console.error(`elevateGlobalAdminToUserAccessAdmin: ${apiResponse.error}`);
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return apiResponse.data.success;
|
|
345
|
+
} catch (error: any) {
|
|
346
|
+
console.error('Error in elevateGlobalAdminToUserAccessAdmin:', error);
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Read Azure resources accessible to the current user
|
|
353
|
+
* Calls backend: GET /api/azuremanagement/resources
|
|
354
|
+
*/
|
|
355
|
+
export async function readResources(): Promise<Resource[]> {
|
|
356
|
+
try {
|
|
357
|
+
const url = '/api/azuremanagement/resources';
|
|
358
|
+
const response = await fetch(url, {
|
|
359
|
+
method: 'GET',
|
|
360
|
+
headers: {
|
|
361
|
+
'Content-Type': 'application/json',
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
if (!response.ok) {
|
|
366
|
+
console.error(`readResources: Request failed with status ${response.status}`);
|
|
367
|
+
return [];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const apiResponse: ApiResponse<ResourcesResponseDto> = await response.json();
|
|
371
|
+
|
|
372
|
+
if (!apiResponse.success || !apiResponse.data) {
|
|
373
|
+
console.error(`readResources: ${apiResponse.error}`);
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return apiResponse.data.resources;
|
|
378
|
+
} catch (error: any) {
|
|
379
|
+
console.error('Error in readResources:', error);
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
}
|