forked from baron/baron-sso
973 lines
22 KiB
TypeScript
973 lines
22 KiB
TypeScript
import apiClient from "./apiClient";
|
|
|
|
export type AuditLog = {
|
|
event_id: string;
|
|
timestamp: string;
|
|
user_id: string;
|
|
event_type: string;
|
|
status: string;
|
|
ip_address: string;
|
|
user_agent: string;
|
|
device_id?: string;
|
|
details?: string;
|
|
};
|
|
|
|
export type AuditLogListResponse = {
|
|
items: AuditLog[];
|
|
limit: number;
|
|
cursor?: string;
|
|
next_cursor?: string;
|
|
};
|
|
|
|
export type TenantSummary = {
|
|
id: string;
|
|
type: string; // 허용 타입: PERSONAL, COMPANY, COMPANY_GROUP, ORGANIZATION, USER_GROUP
|
|
name: string;
|
|
slug: string;
|
|
description: string;
|
|
status: string;
|
|
domains?: string[];
|
|
parentId?: string;
|
|
config?: Record<string, unknown>;
|
|
memberCount: number; // Added member count
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export type TenantCreateRequest = {
|
|
name: string;
|
|
type?: string;
|
|
slug?: string;
|
|
parentId?: string;
|
|
description?: string;
|
|
status?: string;
|
|
domains?: string[];
|
|
forceDomainConflicts?: string[];
|
|
config?: Record<string, unknown>;
|
|
};
|
|
|
|
export type TenantListResponse = {
|
|
items: TenantSummary[];
|
|
limit: number;
|
|
offset: number;
|
|
total: number;
|
|
};
|
|
|
|
export type TenantUpdateRequest = {
|
|
name?: string;
|
|
type?: string;
|
|
slug?: string;
|
|
parentId?: string;
|
|
description?: string;
|
|
status?: string;
|
|
domains?: string[];
|
|
forceDomainConflicts?: string[];
|
|
config?: Record<string, unknown>;
|
|
};
|
|
|
|
export type TenantImportResult = {
|
|
created: number;
|
|
updated: number;
|
|
failed: number;
|
|
errors: string[];
|
|
};
|
|
|
|
export type ApiKeySummary = {
|
|
id: string;
|
|
name: string;
|
|
client_id: string;
|
|
scopes: string[];
|
|
status: string;
|
|
lastUsedAt?: string;
|
|
createdAt: string;
|
|
};
|
|
|
|
export type ApiKeyListResponse = {
|
|
items: ApiKeySummary[];
|
|
total: number;
|
|
};
|
|
|
|
export type RoleSummary = {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
permissions: string[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export type RoleListResponse = {
|
|
items: RoleSummary[];
|
|
total: number;
|
|
};
|
|
|
|
export type RPUsageDailyMetric = {
|
|
date: string;
|
|
tenantId: string;
|
|
tenantType: string;
|
|
tenantName?: string;
|
|
clientId: string;
|
|
clientName: string;
|
|
loginRequests: number;
|
|
otherRequests: number;
|
|
uniqueSubjects: number;
|
|
};
|
|
|
|
export type RPUsagePeriod = "day" | "week" | "month";
|
|
|
|
export type RPUsageDailyResponse = {
|
|
items: RPUsageDailyMetric[];
|
|
days: number;
|
|
period: RPUsagePeriod;
|
|
tenantId?: string;
|
|
};
|
|
|
|
export type AdminOverviewStats = {
|
|
totalTenants: number;
|
|
oidcClients: number;
|
|
auditEvents24h: number;
|
|
};
|
|
|
|
export type UserProjectionStatus = {
|
|
name: string;
|
|
status: "ready" | "failed" | "syncing" | string;
|
|
ready: boolean;
|
|
lastSyncedAt?: string;
|
|
lastError?: string;
|
|
updatedAt?: string;
|
|
projectedUsers: number;
|
|
};
|
|
|
|
export type UserProjectionActionResult = {
|
|
status: string;
|
|
syncedUsers: number;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export async function fetchAuditLogs(limit = 50, cursor?: string) {
|
|
const { data } = await apiClient.get<AuditLogListResponse>("/v1/audit", {
|
|
params: { limit, cursor },
|
|
});
|
|
return data;
|
|
}
|
|
|
|
export async function fetchAdminOverviewStats() {
|
|
const { data } = await apiClient.get<AdminOverviewStats>("/v1/admin/stats");
|
|
return data;
|
|
}
|
|
|
|
export async function fetchUserProjectionStatus() {
|
|
const { data } = await apiClient.get<UserProjectionStatus>(
|
|
"/v1/admin/projections/users",
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function reconcileUserProjection() {
|
|
const { data } = await apiClient.post<UserProjectionActionResult>(
|
|
"/v1/admin/projections/users/reconcile",
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function resetUserProjection() {
|
|
const { data } = await apiClient.post<UserProjectionActionResult>(
|
|
"/v1/admin/projections/users/reset",
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function fetchAdminRPUsageDaily({
|
|
days = 14,
|
|
period = "day",
|
|
tenantId,
|
|
}: {
|
|
days?: number;
|
|
period?: RPUsagePeriod;
|
|
tenantId?: string;
|
|
} = {}) {
|
|
const { data } = await apiClient.get<RPUsageDailyResponse>(
|
|
"/v1/admin/rp-usage/daily",
|
|
{
|
|
params: { days, period, tenantId: tenantId || undefined },
|
|
},
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function fetchTenants(limit = 50, offset = 0, parentId?: string) {
|
|
const { data } = await apiClient.get<TenantListResponse>(
|
|
"/v1/admin/tenants",
|
|
{
|
|
params: { limit, offset, parentId },
|
|
},
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function fetchTenant(tenantId: string) {
|
|
const { data } = await apiClient.get<TenantSummary>(
|
|
`/v1/admin/tenants/${tenantId}`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function createTenant(payload: TenantCreateRequest) {
|
|
const { data } = await apiClient.post<TenantSummary>(
|
|
"/v1/admin/tenants",
|
|
payload,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function updateTenant(
|
|
tenantId: string,
|
|
payload: TenantUpdateRequest,
|
|
) {
|
|
const { data } = await apiClient.put<TenantSummary>(
|
|
`/v1/admin/tenants/${tenantId}`,
|
|
payload,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function deleteTenant(tenantId: string) {
|
|
await apiClient.delete(`/v1/admin/tenants/${tenantId}`);
|
|
}
|
|
|
|
export async function deleteTenantsBulk(ids: string[]) {
|
|
await apiClient.delete("/v1/admin/tenants/bulk", {
|
|
data: { ids },
|
|
});
|
|
}
|
|
|
|
export async function exportTenantsCSV(includeIds = false) {
|
|
const response = await apiClient.get<Blob>("/v1/admin/tenants/export", {
|
|
params: { includeIds },
|
|
responseType: "blob",
|
|
});
|
|
const dispositionHeader = response.headers["content-disposition"];
|
|
const disposition = Array.isArray(dispositionHeader)
|
|
? dispositionHeader[0]
|
|
: String(dispositionHeader ?? "");
|
|
const filenameMatch = disposition?.match(/filename="?([^"]+)"?/i);
|
|
return {
|
|
blob: response.data,
|
|
filename: filenameMatch?.[1] ?? "tenants.csv",
|
|
};
|
|
}
|
|
|
|
export async function importTenantsCSV(file: File) {
|
|
const formData = new FormData();
|
|
formData.append("file", file);
|
|
const { data } = await apiClient.post<TenantImportResult>(
|
|
"/v1/admin/tenants/import",
|
|
formData,
|
|
{
|
|
headers: {
|
|
"Content-Type": "multipart/form-data",
|
|
},
|
|
},
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function approveTenant(tenantId: string) {
|
|
const { data } = await apiClient.post<TenantSummary>(
|
|
`/v1/admin/tenants/${tenantId}/approve`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export type TenantAdmin = {
|
|
id: string;
|
|
name: string;
|
|
email: string;
|
|
};
|
|
|
|
export async function fetchTenantAdmins(tenantId: string) {
|
|
const { data } = await apiClient.get<TenantAdmin[]>(
|
|
`/v1/admin/tenants/${tenantId}/admins`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function addTenantAdmin(tenantId: string, userId: string) {
|
|
await apiClient.post(`/v1/admin/tenants/${tenantId}/admins/${userId}`);
|
|
}
|
|
|
|
export async function removeTenantAdmin(tenantId: string, userId: string) {
|
|
await apiClient.delete(`/v1/admin/tenants/${tenantId}/admins/${userId}`);
|
|
}
|
|
|
|
export async function fetchTenantOwners(tenantId: string) {
|
|
const { data } = await apiClient.get<TenantAdmin[]>(
|
|
`/v1/admin/tenants/${tenantId}/owners`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function addTenantOwner(tenantId: string, userId: string) {
|
|
await apiClient.post(`/v1/admin/tenants/${tenantId}/owners/${userId}`);
|
|
}
|
|
|
|
export async function removeTenantOwner(tenantId: string, userId: string) {
|
|
await apiClient.delete(`/v1/admin/tenants/${tenantId}/owners/${userId}`);
|
|
}
|
|
|
|
// Group Management
|
|
export type GroupMember = {
|
|
id: string;
|
|
name: string;
|
|
email: string;
|
|
};
|
|
|
|
export type GroupSummary = {
|
|
id: string;
|
|
tenantId: string;
|
|
parentId?: string;
|
|
name: string;
|
|
description?: string;
|
|
unitType?: string;
|
|
members?: GroupMember[];
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
};
|
|
|
|
export type GroupCreateRequest = {
|
|
name: string;
|
|
parentId?: string;
|
|
description?: string;
|
|
unitType?: string;
|
|
};
|
|
|
|
export async function fetchGroups(tenantId: string) {
|
|
const { data } = await apiClient.get<GroupSummary[]>(
|
|
`/v1/admin/tenants/${tenantId}/organization`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function fetchGroup(tenantId: string, groupId: string) {
|
|
const { data } = await apiClient.get<GroupSummary>(
|
|
`/v1/admin/tenants/${tenantId}/organization/${groupId}`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function createGroup(
|
|
tenantId: string,
|
|
payload: GroupCreateRequest,
|
|
) {
|
|
const { data } = await apiClient.post<GroupSummary>(
|
|
`/v1/admin/tenants/${tenantId}/organization`,
|
|
payload,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function deleteGroup(tenantId: string, groupId: string) {
|
|
await apiClient.delete(
|
|
`/v1/admin/tenants/${tenantId}/organization/${groupId}`,
|
|
);
|
|
}
|
|
|
|
export async function addGroupMember(
|
|
tenantId: string,
|
|
groupId: string,
|
|
userId: string,
|
|
) {
|
|
await apiClient.post(
|
|
`/v1/admin/tenants/${tenantId}/organization/${groupId}/members`,
|
|
{ userId },
|
|
);
|
|
}
|
|
|
|
export async function removeGroupMember(
|
|
tenantId: string,
|
|
groupId: string,
|
|
userId: string,
|
|
) {
|
|
await apiClient.delete(
|
|
`/v1/admin/tenants/${tenantId}/organization/${groupId}/members/${userId}`,
|
|
);
|
|
}
|
|
|
|
export type GroupRole = {
|
|
tenantId: string;
|
|
tenantName: string;
|
|
relation: string;
|
|
};
|
|
|
|
export async function fetchGroupRoles(tenantId: string, groupId: string) {
|
|
const { data } = await apiClient.get<GroupRole[]>(
|
|
`/v1/admin/tenants/${tenantId}/organization/${groupId}/roles`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function assignGroupRole(
|
|
tenantId: string,
|
|
groupId: string,
|
|
targetTenantId: string,
|
|
relation: string,
|
|
) {
|
|
await apiClient.post(
|
|
`/v1/admin/tenants/${tenantId}/organization/${groupId}/roles`,
|
|
{ tenantId: targetTenantId, relation },
|
|
);
|
|
}
|
|
|
|
export async function removeGroupRole(
|
|
tenantId: string,
|
|
groupId: string,
|
|
targetTenantId: string,
|
|
relation: string,
|
|
) {
|
|
await apiClient.delete(
|
|
`/v1/admin/tenants/${tenantId}/organization/${groupId}/roles/${targetTenantId}/${relation}`,
|
|
);
|
|
}
|
|
|
|
// API Key Management (M2M)
|
|
export type ApiKeyCreateRequest = {
|
|
name: string;
|
|
scopes: string[];
|
|
};
|
|
|
|
export type ApiKeyCreateResponse = {
|
|
apiKey: ApiKeySummary;
|
|
clientSecret: string;
|
|
};
|
|
|
|
export async function fetchApiKeys(limit = 50, offset = 0) {
|
|
const { data } = await apiClient.get<ApiKeyListResponse>(
|
|
"/v1/admin/api-keys",
|
|
{
|
|
params: { limit, offset },
|
|
},
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function createApiKey(payload: ApiKeyCreateRequest) {
|
|
const { data } = await apiClient.post<ApiKeyCreateResponse>(
|
|
"/v1/admin/api-keys",
|
|
payload,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function deleteApiKey(apiKeyId: string) {
|
|
await apiClient.delete(`/v1/admin/api-keys/${apiKeyId}`);
|
|
}
|
|
|
|
// User Management
|
|
export type UserSummary = {
|
|
id: string;
|
|
email: string;
|
|
loginId?: string;
|
|
name: string;
|
|
phone?: string;
|
|
role: string;
|
|
status: string;
|
|
tenantSlug?: string;
|
|
companyCode?: string;
|
|
tenant?: TenantSummary;
|
|
joinedTenants?: TenantSummary[]; // [New] 다중 소속 테넌트 목록
|
|
metadata?: Record<string, unknown>;
|
|
department?: string;
|
|
position?: string;
|
|
jobTitle?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export type UserListResponse = {
|
|
items: UserSummary[];
|
|
limit: number;
|
|
offset: number;
|
|
total: number;
|
|
};
|
|
|
|
export type UserCreateRequest = {
|
|
email: string;
|
|
loginId?: string;
|
|
password?: string;
|
|
name: string;
|
|
phone?: string;
|
|
role?: string;
|
|
tenantSlug?: string;
|
|
department?: string;
|
|
position?: string;
|
|
jobTitle?: string;
|
|
primaryTenantId?: string;
|
|
primaryTenantName?: string;
|
|
primaryTenantIsOwner?: boolean;
|
|
additionalAppointments?: UserAppointment[];
|
|
metadata?: Record<string, unknown>;
|
|
};
|
|
|
|
export type UserCreateResponse = UserSummary & {
|
|
initialPassword?: string;
|
|
};
|
|
|
|
export type UserUpdateRequest = {
|
|
loginId?: string;
|
|
password?: string;
|
|
name?: string;
|
|
phone?: string;
|
|
role?: string;
|
|
status?: string;
|
|
tenantSlug?: string;
|
|
isAddTenant?: boolean;
|
|
isRemoveTenant?: boolean;
|
|
department?: string;
|
|
position?: string;
|
|
jobTitle?: string;
|
|
primaryTenantId?: string;
|
|
primaryTenantName?: string;
|
|
primaryTenantIsOwner?: boolean;
|
|
additionalAppointments?: UserAppointment[];
|
|
metadata?: Record<string, unknown>;
|
|
};
|
|
|
|
export type UserAppointment = {
|
|
tenantId: string;
|
|
tenantSlug?: string;
|
|
tenantName: string;
|
|
isPrimary?: boolean;
|
|
isOwner: boolean;
|
|
jobTitle?: string;
|
|
position?: string;
|
|
};
|
|
|
|
export type BulkUserItem = {
|
|
email: string;
|
|
loginId?: string;
|
|
name: string;
|
|
phone?: string;
|
|
role?: string;
|
|
tenantSlug?: string;
|
|
department?: string;
|
|
position?: string;
|
|
jobTitle?: string;
|
|
tenantImport?: {
|
|
sourceTenantId?: string;
|
|
slug?: string;
|
|
name?: string;
|
|
type?: string;
|
|
parentTenantId?: string;
|
|
parentTenantSlug?: string;
|
|
parentTenantName?: string;
|
|
memo?: string;
|
|
emailDomain?: string;
|
|
};
|
|
metadata: Record<string, string>;
|
|
};
|
|
|
|
export type BulkUserResult = {
|
|
email: string;
|
|
originalEmail?: string;
|
|
suggestedEmail?: string;
|
|
status?: string;
|
|
warnings?: string[];
|
|
success: boolean;
|
|
message?: string;
|
|
userId?: string;
|
|
};
|
|
|
|
export type BulkUserResponse = {
|
|
results: BulkUserResult[];
|
|
};
|
|
|
|
export type WorksmobileOutboxItem = {
|
|
id: string;
|
|
resourceType: string;
|
|
resourceId: string;
|
|
action: string;
|
|
status: string;
|
|
retryCount: number;
|
|
lastError?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export type WorksmobileOverview = {
|
|
tenant: TenantSummary;
|
|
config: {
|
|
enabled: boolean;
|
|
domainMappings?: Record<string, number>;
|
|
tokenConfigured: boolean;
|
|
adminTenantId?: string;
|
|
};
|
|
recentJobs: WorksmobileOutboxItem[];
|
|
};
|
|
|
|
export type WorksmobileComparisonItem = {
|
|
resourceType: string;
|
|
baronId?: string;
|
|
baronName?: string;
|
|
baronEmail?: string;
|
|
baronPrimaryOrgId?: string;
|
|
baronPrimaryOrgName?: string;
|
|
baronParentId?: string;
|
|
baronParentName?: string;
|
|
worksmobileId?: string;
|
|
externalKey?: string;
|
|
worksmobileName?: string;
|
|
worksmobileEmail?: string;
|
|
worksmobileLevelId?: string;
|
|
worksmobileLevelName?: string;
|
|
worksmobileTask?: string;
|
|
worksmobileDomainId?: number;
|
|
worksmobileDomainName?: string;
|
|
worksmobilePrimaryOrgId?: string;
|
|
worksmobilePrimaryOrgName?: string;
|
|
worksmobilePrimaryOrgPositionId?: string;
|
|
worksmobilePrimaryOrgPositionName?: string;
|
|
worksmobilePrimaryOrgIsManager?: boolean;
|
|
worksmobileParentId?: string;
|
|
worksmobileParentName?: string;
|
|
status: string;
|
|
};
|
|
|
|
export type WorksmobileComparison = {
|
|
users: WorksmobileComparisonItem[];
|
|
groups: WorksmobileComparisonItem[];
|
|
};
|
|
|
|
export async function fetchUsers(
|
|
limit = 50,
|
|
offset = 0,
|
|
search?: string,
|
|
tenantSlug?: string,
|
|
) {
|
|
const { data } = await apiClient.get<UserListResponse>("/v1/admin/users", {
|
|
params: { limit, offset, search, tenantSlug },
|
|
});
|
|
return data;
|
|
}
|
|
|
|
export async function fetchUser(userId: string) {
|
|
const { data } = await apiClient.get<UserSummary>(
|
|
`/v1/admin/users/${userId}`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function createUser(payload: UserCreateRequest) {
|
|
// Map tenantSlug to companyCode for backend compatibility
|
|
const requestPayload: UserCreateRequest & { companyCode?: string } = {
|
|
...payload,
|
|
};
|
|
if (payload.tenantSlug !== undefined) {
|
|
requestPayload.companyCode = payload.tenantSlug;
|
|
}
|
|
|
|
const { data } = await apiClient.post<UserCreateResponse>(
|
|
"/v1/admin/users",
|
|
requestPayload,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function exportUsersCSV(
|
|
search?: string,
|
|
tenantSlug?: string,
|
|
includeIds = false,
|
|
) {
|
|
const response = await apiClient.get<Blob>("/v1/admin/users/export", {
|
|
params: { search, tenantSlug, includeIds },
|
|
responseType: "blob",
|
|
});
|
|
const dispositionHeader = response.headers["content-disposition"];
|
|
const disposition = Array.isArray(dispositionHeader)
|
|
? dispositionHeader[0]
|
|
: String(dispositionHeader ?? "");
|
|
const filenameMatch = disposition?.match(/filename="?([^"]+)"?/i);
|
|
return {
|
|
blob: response.data,
|
|
filename: filenameMatch?.[1] ?? "users.csv",
|
|
};
|
|
}
|
|
|
|
export async function bulkCreateUsers(users: BulkUserItem[]) {
|
|
const mappedUsers = users.map((u) => {
|
|
const mapped: BulkUserItem & { companyCode?: string } = { ...u };
|
|
if (u.tenantSlug !== undefined) {
|
|
mapped.companyCode = u.tenantSlug;
|
|
}
|
|
return mapped;
|
|
});
|
|
const { data } = await apiClient.post<BulkUserResponse>(
|
|
"/v1/admin/users/bulk",
|
|
{ users: mappedUsers },
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function fetchWorksmobileOverview(tenantId: string) {
|
|
const { data } = await apiClient.get<WorksmobileOverview>(
|
|
`/v1/admin/tenants/${tenantId}/worksmobile`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function fetchWorksmobileComparison(
|
|
tenantId: string,
|
|
includeMatched = false,
|
|
) {
|
|
const { data } = await apiClient.get<WorksmobileComparison>(
|
|
`/v1/admin/tenants/${tenantId}/worksmobile/comparison`,
|
|
{
|
|
params: { includeMatched },
|
|
},
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function downloadWorksmobileInitialPasswordsCSV(tenantId: string) {
|
|
const response = await apiClient.get<Blob>(
|
|
`/v1/admin/tenants/${tenantId}/worksmobile/initial-passwords.csv`,
|
|
{
|
|
responseType: "blob",
|
|
},
|
|
);
|
|
const dispositionHeader = response.headers["content-disposition"];
|
|
const disposition = Array.isArray(dispositionHeader)
|
|
? dispositionHeader[0]
|
|
: String(dispositionHeader ?? "");
|
|
const filenameMatch = disposition?.match(/filename="?([^"]+)"?/i);
|
|
return {
|
|
blob: response.data,
|
|
filename: filenameMatch?.[1] ?? "worksmobile_initial_passwords.csv",
|
|
};
|
|
}
|
|
|
|
export async function enqueueWorksmobileBackfillDryRun(tenantId: string) {
|
|
const { data } = await apiClient.post(
|
|
`/v1/admin/tenants/${tenantId}/worksmobile/backfill/dry-run`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function enqueueWorksmobileOrgUnitSync(
|
|
tenantId: string,
|
|
orgUnitId: string,
|
|
) {
|
|
const { data } = await apiClient.post<WorksmobileOutboxItem>(
|
|
`/v1/admin/tenants/${tenantId}/worksmobile/orgunits/${orgUnitId}/sync`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function enqueueWorksmobileUserSync(
|
|
tenantId: string,
|
|
userId: string,
|
|
) {
|
|
const { data } = await apiClient.post<WorksmobileOutboxItem>(
|
|
`/v1/admin/tenants/${tenantId}/worksmobile/users/${userId}/sync`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function retryWorksmobileJob(tenantId: string, jobId: string) {
|
|
const { data } = await apiClient.post(
|
|
`/v1/admin/tenants/${tenantId}/worksmobile/jobs/${jobId}/retry`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function bulkUpdateUsers(payload: {
|
|
userIds: string[];
|
|
status?: string;
|
|
role?: string;
|
|
tenantSlug?: string;
|
|
department?: string;
|
|
position?: string;
|
|
jobTitle?: string;
|
|
}) {
|
|
const requestPayload: typeof payload & { companyCode?: string } = {
|
|
...payload,
|
|
};
|
|
if (payload.tenantSlug !== undefined) {
|
|
requestPayload.companyCode = payload.tenantSlug;
|
|
}
|
|
const { data } = await apiClient.put("/v1/admin/users/bulk", requestPayload);
|
|
return data;
|
|
}
|
|
|
|
export async function bulkDeleteUsers(userIds: string[]) {
|
|
const { data } = await apiClient.delete("/v1/admin/users/bulk", {
|
|
data: { userIds },
|
|
});
|
|
return data;
|
|
}
|
|
|
|
export async function updateUser(userId: string, payload: UserUpdateRequest) {
|
|
const requestPayload: UserUpdateRequest & { companyCode?: string } = {
|
|
...payload,
|
|
};
|
|
if (payload.tenantSlug !== undefined) {
|
|
requestPayload.companyCode = payload.tenantSlug;
|
|
}
|
|
|
|
const { data } = await apiClient.put<UserSummary>(
|
|
`/v1/admin/users/${userId}`,
|
|
requestPayload,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export type PasswordPolicyResponse = {
|
|
minLength?: number;
|
|
lowercase?: boolean;
|
|
uppercase?: boolean;
|
|
number?: boolean;
|
|
nonAlphanumeric?: boolean;
|
|
minCharacterTypes?: number;
|
|
};
|
|
|
|
export async function fetchPasswordPolicy() {
|
|
const { data } = await apiClient.get<PasswordPolicyResponse>(
|
|
"/v1/auth/password/policy",
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function deleteUser(userId: string) {
|
|
await apiClient.delete(`/v1/admin/users/${userId}`);
|
|
}
|
|
|
|
export type UserRpHistoryItem = {
|
|
client_id: string;
|
|
client_name: string;
|
|
lastLoginAt: string;
|
|
status: string;
|
|
};
|
|
|
|
export async function fetchUserRpHistory(userId: string) {
|
|
const { data } = await apiClient.get<UserRpHistoryItem[]>(
|
|
`/v1/admin/users/${userId}/rp-history`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export type UserProfileResponse = {
|
|
id: string;
|
|
email: string;
|
|
name: string;
|
|
phone: string;
|
|
role: string;
|
|
department: string;
|
|
affiliationType: string;
|
|
tenantSlug?: string;
|
|
tenantId?: string;
|
|
metadata?: Record<string, unknown>;
|
|
tenant?: TenantSummary;
|
|
manageableTenants?: TenantSummary[];
|
|
};
|
|
|
|
export async function fetchMe() {
|
|
const { data } = await apiClient.get<UserProfileResponse>("/v1/user/me");
|
|
return data;
|
|
}
|
|
|
|
// Relying Party Management
|
|
export type RelyingParty = {
|
|
clientId: string;
|
|
tenantId: string;
|
|
name: string;
|
|
description: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export type HydraClientReq = {
|
|
client_id?: string;
|
|
client_name: string;
|
|
client_secret?: string;
|
|
redirect_uris: string[];
|
|
scope?: string;
|
|
token_endpoint_auth_method?: string;
|
|
grant_types?: string[];
|
|
response_types?: string[];
|
|
metadata?: Record<string, unknown>;
|
|
};
|
|
|
|
export async function fetchRelyingParties(tenantId: string) {
|
|
const { data } = await apiClient.get<RelyingParty[]>(
|
|
`/v1/admin/tenants/${tenantId}/relying-parties`,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function fetchAllRelyingParties() {
|
|
const { data } = await apiClient.get<RelyingParty[]>(
|
|
"/v1/admin/relying-parties",
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function createRelyingParty(
|
|
tenantId: string,
|
|
payload: HydraClientReq,
|
|
) {
|
|
const { data } = await apiClient.post<RelyingParty>(
|
|
`/v1/admin/tenants/${tenantId}/relying-parties`,
|
|
payload,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function fetchRelyingParty(id: string) {
|
|
const { data } = await apiClient.get<{
|
|
relyingParty: RelyingParty;
|
|
oauth2Config: HydraClientReq;
|
|
}>(`/v1/admin/relying-parties/${id}`);
|
|
return data;
|
|
}
|
|
|
|
export async function updateRelyingParty(id: string, payload: HydraClientReq) {
|
|
const { data } = await apiClient.put<RelyingParty>(
|
|
`/v1/admin/relying-parties/${id}`,
|
|
payload,
|
|
);
|
|
return data;
|
|
}
|
|
|
|
export async function deleteRelyingParty(id: string) {
|
|
await apiClient.delete(`/v1/admin/relying-parties/${id}`);
|
|
}
|
|
|
|
export type RPOwner = {
|
|
subject: string;
|
|
|
|
name?: string;
|
|
|
|
email?: string;
|
|
|
|
type: string;
|
|
};
|
|
|
|
export async function fetchRPOwners(clientId: string) {
|
|
const { data } = await apiClient.get<RPOwner[]>(
|
|
`/v1/admin/relying-parties/${clientId}/owners`,
|
|
);
|
|
|
|
return data;
|
|
}
|
|
|
|
export async function addRPOwner(clientId: string, subject: string) {
|
|
await apiClient.post(
|
|
`/v1/admin/relying-parties/${clientId}/owners/${subject}`,
|
|
);
|
|
}
|
|
|
|
export async function removeRPOwner(clientId: string, subject: string) {
|
|
await apiClient.delete(
|
|
`/v1/admin/relying-parties/${clientId}/owners/${subject}`,
|
|
);
|
|
}
|