Files
oci-portal-dash/src/api/tenant.ts
T
Wang Defa 5464d2dfea
CI / test (push) Successful in 27s
Release / release (push) Successful in 26s
发布 0.1.0:通知渠道、告警规则与多项体验修复
2026-07-10 17:31:19 +08:00

358 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
mockAuditEvents,
mockIdentitySetting,
mockIdps,
mockLimits,
mockPolicies,
mockRecipients,
mockSignOnRules,
mockSubscriptions,
mockUserDetails,
mockUsers,
} from './mock'
import { mockOn, mocked, request } from './request'
import type {
AuditEventsResult,
IamUser,
IdentityDomain,
IamUserDetail,
IdentityProvider,
IdentitySetting,
LimitItem,
LimitService,
NotificationRecipients,
PasswordPolicy,
SignOnRule,
SubscriptionDetail,
SubscriptionSummary,
} from '@/types/api'
// ---- 配额 ----
export interface LimitsQuery {
service?: string
region?: string
name?: string
scopeType?: 'GLOBAL' | 'REGION' | 'AD'
availabilityDomain?: string
/** 服务端过滤上限为 0 的配额项,查用量时不占并发名额 */
nonZero?: boolean
withAvailability?: boolean
}
export function listLimits(id: number, query: LimitsQuery = {}): Promise<LimitItem[]> {
if (mockOn) {
if (query.service === 'regions')
return mocked([{ name: 'subscribed-region-count', scopeType: 'GLOBAL', value: 3 }])
const needle = (query.name ?? '').toLowerCase()
return mocked(
mockLimits
.filter((l) => !needle || l.name.toLowerCase().includes(needle))
.filter((l) => !query.scopeType || l.scopeType === query.scopeType)
.filter((l) => !query.nonZero || l.value > 0),
)
}
return request(`/oci-configs/${id}/limits`, { query: { ...query } })
}
export function listLimitServices(id: number): Promise<LimitService[]> {
if (mockOn)
return mocked([
{ name: 'compute', description: 'Compute' },
{ name: 'vcn', description: 'Virtual Cloud Network' },
{ name: 'block-storage', description: 'Block Volume' },
])
return request(`/oci-configs/${id}/limits/services`)
}
// ---- 订阅 ----
export function listSubscriptions(id: number): Promise<SubscriptionSummary[]> {
if (mockOn)
return mocked(
mockSubscriptions.map((d) => ({
id: d.id,
serviceName: d.serviceName,
timeCreated: d.timeCreated,
timeUpdated: d.timeUpdated,
})),
)
return request(`/oci-configs/${id}/subscriptions`)
}
export function getSubscription(id: number, subscriptionId: string): Promise<SubscriptionDetail> {
if (mockOn)
return mocked(mockSubscriptions.find((d) => d.id === subscriptionId) ?? mockSubscriptions[0])
return request(`/oci-configs/${id}/subscriptions/${subscriptionId}`)
}
// ---- 身份域 ----
/** 租户 ACTIVE 身份域列表;空数组表示无域老租户(隐藏选择器走经典路径) */
export function listIdentityDomains(id: number): Promise<IdentityDomain[]> {
if (mockOn)
return mocked([
{ id: 'ocid1.domain.default', displayName: 'Default', homeRegion: 'us-phoenix-1', type: 'DEFAULT', licenseType: 'free' },
{ id: 'ocid1.domain.idcs', displayName: 'OracleIdentityCloudService', homeRegion: 'us-phoenix-1', type: 'SECONDARY', licenseType: 'idcs-foundation' },
])
return request(`/oci-configs/${id}/domains`)
}
// ---- 用户 ----
export function listUsers(id: number, domainId?: string): Promise<IamUser[]> {
if (mockOn) return mocked(mockUsers)
return request(`/oci-configs/${id}/users`, { query: { domainId } })
}
// 编辑表单预填充用;不在身份域的经典用户返回 inDomain: false
export function getUserDetail(id: number, userId: string, domainId?: string): Promise<IamUserDetail> {
if (mockOn)
return mocked(
mockUserDetails[userId] ?? {
inDomain: false,
givenName: '',
familyName: '',
isDomainAdmin: false,
inAdminGroup: false,
},
)
return request(`/oci-configs/${id}/users/${userId}`, { query: { domainId } })
}
export interface CreateUserBody {
name: string
givenName: string
familyName: string
description?: string
email?: string
/** 同时勾选时后端先授身份域管理员,再加入管理员组 */
grantDomainAdmin?: boolean
addToAdminGroup?: boolean
}
export function createUser(id: number, body: CreateUserBody, domainId?: string): Promise<IamUser> {
if (mockOn)
return mocked({ ...mockUsers[2], id: 'ocid1.user..new', name: body.name, email: body.email ?? '' })
return request(`/oci-configs/${id}/users`, { method: 'POST', body, query: { domainId } })
}
export interface UpdateUserBody {
description?: string
email?: string
givenName?: string
familyName?: string
/** 三态:true 授予/加入,false 撤销/移出,缺省不动 */
grantDomainAdmin?: boolean
addToAdminGroup?: boolean
}
export function updateUser(id: number, userId: string, body: UpdateUserBody, domainId?: string): Promise<IamUser> {
if (mockOn) {
const u = mockUsers.find((x) => x.id === userId) ?? mockUsers[2]
if (body.description !== undefined) u.description = body.description
if (body.email !== undefined) u.email = body.email
return mocked({ ...u })
}
return request(`/oci-configs/${id}/users/${userId}`, { method: 'PUT', body, query: { domainId } })
}
export function deleteUser(id: number, userId: string, domainId?: string): Promise<void> {
if (mockOn) return mocked(undefined)
return request(`/oci-configs/${id}/users/${userId}`, { method: 'DELETE', query: { domainId } })
}
export function resetUserPassword(id: number, userId: string, domainId?: string): Promise<{ password: string }> {
if (mockOn) return mocked({ password: 'Xy9#kQ2m$Lp8' })
return request(`/oci-configs/${id}/users/${userId}/reset-password`, { method: 'POST', query: { domainId } })
}
export function clearUserMfa(id: number, userId: string, domainId?: string): Promise<{ deletedTotpDevices: number }> {
if (mockOn) return mocked({ deletedTotpDevices: 1 })
return request(`/oci-configs/${id}/users/${userId}/mfa-devices`, { method: 'DELETE', query: { domainId } })
}
export function deleteUserApiKeys(
id: number,
userId: string,
includeCurrent = false,
): Promise<{ deletedApiKeys: number }> {
if (mockOn) return mocked({ deletedApiKeys: 1 })
return request(`/oci-configs/${id}/users/${userId}/api-keys`, {
method: 'DELETE',
query: { includeCurrent },
})
}
// ---- 审计日志 ----
/** 批式懒加载查询审计事件:cursor 空自当前时刻首查,
* 非空从上次响应游标向更早续取一批(~100 条,服务端分窗回溯) */
export function listAuditEvents(
id: number,
opts: { region?: string; cursor?: string; limit?: number } = {},
): Promise<AuditEventsResult> {
// mock:首批带游标,续查一批后到尽头,演示懒加载链路
if (mockOn)
return mocked(
{ items: mockAuditEvents, cursor: opts.cursor ? undefined : 'mock-cursor', exhausted: !!opts.cursor },
300,
)
return request(`/oci-configs/${id}/audit-events`, { query: { ...opts } })
}
/** 取回单条审计事件的原始 JSON;404 表示缓存过期且重查未找回(前端降级展示精简字段) */
export function getAuditEventDetail(
id: number,
q: { eventId: string; eventTime: string; region?: string },
): Promise<{ raw: unknown }> {
if (mockOn) return mocked({ raw: { eventId: q.eventId, mock: true, note: '演示原始事件' } }, 200)
return request(`/oci-configs/${id}/audit-events/detail`, { query: q })
}
// ---- 域设置 ----
export function getNotificationRecipients(id: number, domainId?: string): Promise<NotificationRecipients> {
if (mockOn) return mocked(mockRecipients)
return request(`/oci-configs/${id}/notification-recipients`, { query: { domainId } })
}
export function updateNotificationRecipients(
id: number,
recipients: string[],
domainId?: string,
): Promise<NotificationRecipients> {
if (mockOn) return mocked({ recipients, testModeEnabled: recipients.length > 0 })
return request(`/oci-configs/${id}/notification-recipients`, {
method: 'PUT',
body: { recipients },
query: { domainId },
})
}
export function listPasswordPolicies(id: number, domainId?: string): Promise<PasswordPolicy[]> {
if (mockOn) return mocked(mockPolicies)
return request(`/oci-configs/${id}/password-policies`, { query: { domainId } })
}
export function updatePasswordPolicy(
id: number,
policyId: string,
body: { passwordExpiresAfter?: number; minLength?: number; numPasswordsInHistory?: number },
domainId?: string,
): Promise<PasswordPolicy> {
if (mockOn) return mocked({ ...mockPolicies[0], ...body })
return request(`/oci-configs/${id}/password-policies/${policyId}`, { method: 'PUT', body, query: { domainId } })
}
// ---- 身份设置 ----
export function getIdentitySetting(id: number, domainId?: string): Promise<IdentitySetting> {
if (mockOn) return mocked({ ...mockIdentitySetting })
return request(`/oci-configs/${id}/identity-settings`, { query: { domainId } })
}
export function updateIdentitySetting(
id: number,
primaryEmailRequired: boolean,
domainId?: string,
): Promise<IdentitySetting> {
if (mockOn) {
mockIdentitySetting.primaryEmailRequired = primaryEmailRequired
return mocked({ ...mockIdentitySetting })
}
return request(`/oci-configs/${id}/identity-settings`, {
method: 'PUT',
body: { primaryEmailRequired },
query: { domainId },
})
}
export async function downloadSamlMetadata(id: number, domainId?: string): Promise<void> {
if (mockOn) {
const blob = new Blob(['<mock-saml-metadata/>'], { type: 'application/xml' })
triggerDownload(blob, 'oci-domain-saml-metadata.xml')
return
}
const { useAuthStore } = await import('@/stores/auth')
const auth = useAuthStore()
const qs = domainId ? `?domainId=${encodeURIComponent(domainId)}` : ''
const resp = await fetch(`/api/v1/oci-configs/${id}/saml-metadata${qs}`, {
headers: auth.token ? { Authorization: `Bearer ${auth.token}` } : {},
})
if (!resp.ok) throw new Error(`下载失败(${resp.status}`)
triggerDownload(await resp.blob(), 'oci-domain-saml-metadata.xml')
}
function triggerDownload(blob: Blob, filename: string) {
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = filename
a.click()
URL.revokeObjectURL(url)
}
// ---- Federation ----
export interface CreateIdpBody {
name: string
metadata: string
description?: string
/** logodata URI 或外链 URL */
iconUrl?: string
nameIdFormat?: string
/** 非空表示按断言属性映射;空则按 SAML NameID 映射 */
assertionAttribute?: string
userStoreAttribute?: string
jitEnabled?: boolean
jitCreate?: boolean
jitUpdate?: boolean
jitAssignAdminGroup?: boolean
jitMapEmail?: boolean
}
export function createIdp(id: number, body: CreateIdpBody, domainId?: string): Promise<IdentityProvider> {
if (mockOn) {
const created = {
...mockIdps[1],
id: `idp-${mockIdps.length + 1}`,
name: body.name,
enabled: false,
}
mockIdps.push(created)
return mocked(created, 900)
}
return request(`/oci-configs/${id}/identity-providers`, { method: 'POST', body, query: { domainId } })
}
export function listIdps(id: number, domainId?: string): Promise<IdentityProvider[]> {
if (mockOn) return mocked(mockIdps)
return request(`/oci-configs/${id}/identity-providers`, { query: { domainId } })
}
export function activateIdp(id: number, idpId: string, enabled: boolean, domainId?: string): Promise<IdentityProvider> {
if (mockOn) return mocked({ ...mockIdps[0], enabled })
return request(`/oci-configs/${id}/identity-providers/${idpId}/activate`, {
method: 'POST',
body: { enabled },
query: { domainId },
})
}
export function deleteIdp(id: number, idpId: string, domainId?: string): Promise<void> {
if (mockOn) return mocked(undefined)
return request(`/oci-configs/${id}/identity-providers/${idpId}`, { method: 'DELETE', query: { domainId } })
}
export function listSignOnRules(id: number, domainId?: string): Promise<SignOnRule[]> {
if (mockOn) return mocked(mockSignOnRules)
return request(`/oci-configs/${id}/sign-on-rules`, { query: { domainId } })
}
export function createSignOnExemption(id: number, identityProviderId: string, domainId?: string): Promise<SignOnRule> {
if (mockOn) return mocked(mockSignOnRules[0])
return request(`/oci-configs/${id}/sign-on-exemptions`, {
method: 'POST',
body: { identityProviderId },
query: { domainId },
})
}
export function deleteSignOnExemption(id: number, ruleId: string, domainId?: string): Promise<void> {
if (mockOn) return mocked(undefined)
return request(`/oci-configs/${id}/sign-on-exemptions/${ruleId}`, { method: 'DELETE', query: { domainId } })
}