@autional-cn/shared 0.1.0-rc.4 → 0.1.0-rc.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autional-cn/shared",
3
- "version": "0.1.0-rc.4",
3
+ "version": "0.1.0-rc.6",
4
4
  "description": "Autional shared layer — API client, auth, types, hooks, utils",
5
5
  "license": "AGPL-3.0",
6
6
  "main": "./src/index.ts",
@@ -0,0 +1,27 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { needsNamespaceHeader } from '../namespace';
3
+
4
+ describe('needsNamespaceHeader', () => {
5
+ it('密钥 admin 组端点(列表/子路径/查询)需注 X-Namespace', () => {
6
+ expect(needsNamespaceHeader('/secret/api/v1/admin/secrets')).toBe(true);
7
+ expect(needsNamespaceHeader('/secret/api/v1/admin/secrets/detail?key=a/b')).toBe(true);
8
+ expect(needsNamespaceHeader('/secret/api/v1/admin/secrets/batch-delete')).toBe(true);
9
+ expect(needsNamespaceHeader('/secret/api/v1/admin/secrets/policy')).toBe(true);
10
+ });
11
+
12
+ it('非密钥 admin 端点不注头', () => {
13
+ expect(needsNamespaceHeader('/notification/api/v1/admin/announcements')).toBe(false);
14
+ expect(needsNamespaceHeader('/tenant/api/v1/admin/tenants')).toBe(false);
15
+ });
16
+
17
+ it('secret 公开面/internal 面不注头(前端不消费)', () => {
18
+ expect(needsNamespaceHeader('/secret/public/jwt/public-key')).toBe(false);
19
+ expect(needsNamespaceHeader('/secret/api/v1/secret/public/transmission/public-key')).toBe(false);
20
+ expect(needsNamespaceHeader('/api/v1/internal/secret/jwt/keys')).toBe(false);
21
+ });
22
+
23
+ it('相对路径缺省(undefined/空)不注头', () => {
24
+ expect(needsNamespaceHeader(undefined)).toBe(false);
25
+ expect(needsNamespaceHeader('')).toBe(false);
26
+ });
27
+ });
package/src/api/client.ts CHANGED
@@ -9,9 +9,10 @@
9
9
  import axios, { AxiosError } from 'axios';
10
10
  import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
11
11
  import { camelCaseKeys, snakeCaseKeys } from '../utils/case';
12
- import { getApiBaseUrl } from '../config';
12
+ import { API_NAMESPACE, getApiBaseUrl } from '../config';
13
13
  import { getTraceParentHeader } from './trace';
14
14
  import { ENTRY_PLANE_HEADER, resolveEntryPlane } from './entry-plane';
15
+ import { NAMESPACE_HEADER, needsNamespaceHeader } from './namespace';
15
16
  import { AuthService } from '../auth/service';
16
17
 
17
18
  function setupInterceptors(instance: AxiosInstance) {
@@ -39,6 +40,11 @@ function setupInterceptors(instance: AxiosInstance) {
39
40
  config.headers['X-Tenant-ID'] = tenantId;
40
41
  }
41
42
 
43
+ // secret 管理面(§13-A P1-B):admin 组端点 fail-closed 要求 X-Namespace ∈ S
44
+ if (needsNamespaceHeader(config.url)) {
45
+ config.headers[NAMESPACE_HEADER] = API_NAMESPACE;
46
+ }
47
+
42
48
  // 请求体 camelCase → snake_case 转换(后端 Go 期望 snake_case)
43
49
  if (config.data && typeof config.data === 'object' && !(config.data instanceof FormData)) {
44
50
  config.data = snakeCaseKeys(config.data);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * secret 服务管理面 namespace(§13-A P1-B / SEC-204/207):
3
+ * `/secret/api/v1/admin*` 端点 fail-closed 要求 `X-Namespace ∈ 实例服务集合 S`
4
+ * (NamespaceMiddleware,缺头/未服务 → 400)。值取部署配置 VITE_API_NAMESPACE
5
+ * (见 config,缺省 "dev" 与服务端 DefaultNamespace 对称)。
6
+ */
7
+
8
+ export const NAMESPACE_HEADER = 'X-Namespace';
9
+
10
+ export const SECRET_ADMIN_PATH_PREFIX = '/secret/api/v1/admin';
11
+
12
+ export function needsNamespaceHeader(url?: string): boolean {
13
+ return !!url && url.includes(SECRET_ADMIN_PATH_PREFIX);
14
+ }
@@ -356,6 +356,10 @@ export function getPLATFORM_CONSOLE_URL(): string {
356
356
  }
357
357
  export const API_BASE_URL = getEnv('VITE_API_BASE_URL', '/bff');
358
358
 
359
+ // secret 管理面 namespace(§13-A P1-B):admin 组端点要求 X-Namespace ∈ S(fail-closed);
360
+ // 缺省 "dev" 与服务端 DefaultNamespace(service-core cross/registry keyid.go)对称
361
+ export const API_NAMESPACE = getEnv('VITE_API_NAMESPACE', 'dev');
362
+
359
363
  export function getApiBaseUrl(): string {
360
364
  return API_BASE_URL;
361
365
  }
@@ -1,6 +1,6 @@
1
1
  // Auto-generated from swagger.json annotations
2
2
  // DO NOT EDIT — run `python scripts/generate_api_ts.py` to regenerate
3
- // Generated: 2026-08-22 19:20:02
3
+ // Generated: 2026-09-30 17:04:31
4
4
 
5
5
  // @ts-nocheck — auto-generated; validated by check-generated-api.py
6
6
  import type * as Types from './types';
@@ -6855,6 +6855,20 @@ export async function mfaWebauthnCredentialsByCredentialsPut(webauthnCredentialI
6855
6855
  // Notification Service
6856
6856
  // ============================================================
6857
6857
 
6858
+ /**
6859
+ * 管理端列出公告
6860
+ * 管理端分页查询本租户公告列表,支持按状态过滤和关键词搜索;与用户面 /announcements 同体(同租户范围),走 admin 受众供控制台读取 参考:CAN-SPAM Act (15 U.S.C. §7701) — Commercial Email Compliance。
6861
+ */
6862
+ export async function adminAnnouncements(params?: {
6863
+ page?: number; // 页码
6864
+ page_size?: number; // 每页条数
6865
+ status?: string; // 状态过滤: draft/scheduled/published/expired
6866
+ search?: string; // 搜索关键词
6867
+ }) {
6868
+ const res = await api.get(`/notification/api/v1/admin/announcements`, { params });
6869
+ return res.data;
6870
+ }
6871
+
6858
6872
  /**
6859
6873
  * 创建公告
6860
6874
  * 创建新的公告,支持草稿/定时发布,可指定目标租户和角色 参考:CAN-SPAM Act (15 U.S.C. §7701) — Commercial Email Compliance。
@@ -7224,15 +7238,25 @@ export async function notificationsStats() {
7224
7238
 
7225
7239
  /**
7226
7240
  * SSE实时通知流
7227
- * 通过Server-Sent Events实时推送通知,客户端需传入user_id查询参数进行订阅 参考:CAN-SPAM Act (15 U.S.C. §7701) — Commercial Email Compliance。
7241
+ * 通过Server-Sent Events实时推送通知。P5-4:经 POST /notifications/stream-ticket 换取一次性票据后以 ?ticket= 建流(浏览器 EventSource 主路径);非浏览器客户端可走 Authorization 头 JWT(?token= 兼容回退已删除)。订阅身份以认证上下文(票据/JWT claims)为准。 参考:CAN-SPAM Act (15 U.S.C. §7701) — Commercial Email Compliance。
7228
7242
  */
7229
7243
  export async function notificationsStream(params?: {
7230
- user_id: string; // 用户ID
7244
+ ticket?: string; // 一次性短期票据(P5-4 主路径,60s 内一次性消费)
7245
+ user_id?: string; // 用户ID(已弃用回退:仅在认证上下文无用户时使用)
7231
7246
  }) {
7232
7247
  const res = await api.get(`/notification/api/v1/notifications/stream`, { params });
7233
7248
  return res.data;
7234
7249
  }
7235
7250
 
7251
+ /**
7252
+ * 签发 SSE 短期票据
7253
+ * 为 /notifications/stream 签发一次性票据(P5-4):浏览器 EventSource 不能携带 Authorization 头,故以 JWT 保护的本端点换取 60s 一次性票据,再以 ?ticket= 建流,避免 access token 进入 URL/接入日志。需 JWT 认证(正常 Authorization 头调用)。
7254
+ */
7255
+ export async function notificationsStreamTicketPost() {
7256
+ const res = await api.post(`/notification/api/v1/notifications/stream-ticket`);
7257
+ return res.data;
7258
+ }
7259
+
7236
7260
  /**
7237
7261
  * 列出通知模板
7238
7262
  * 获取所有通知模板列表,支持包含已停用模板的选项 参考:CAN-SPAM Act (15 U.S.C. §7701) — Commercial Email Compliance。
@@ -7377,7 +7401,7 @@ export async function pushSubscriptionsBySubscriptions(subscriptionId: string) {
7377
7401
 
7378
7402
  /**
7379
7403
  * 获取 VAPID 公钥
7380
- * 返回 Web Push VAPID 公钥,供前端注册 Push 订阅时使用 参考:CAN-SPAM Act (15 U.S.C. §7701) — Commercial Email Compliance。
7404
+ * 返回 Web Push VAPID 公钥,供前端注册 Push 订阅时使用(来源:communication-service) 参考:CAN-SPAM Act (15 U.S.C. §7701) — Commercial Email Compliance。
7381
7405
  */
7382
7406
  export async function pushVapidPublicKey() {
7383
7407
  const res = await api.get(`/notification/api/v1/push/vapid-public-key`);
@@ -1,6 +1,6 @@
1
1
  // Auto-generated from swagger.json annotations
2
2
  // DO NOT EDIT — run `python scripts/generate_api_ts.py` to regenerate
3
- // Generated: 2026-08-22 19:20:02
3
+ // Generated: 2026-09-30 17:04:31
4
4
 
5
5
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
6
6
  import * as api from './api';
@@ -2599,6 +2599,14 @@ export function useMfaWebauthnCredentialsByCredentials(webauthnCredentialId: str
2599
2599
 
2600
2600
  // --- notification-service ---
2601
2601
 
2602
+ /** 管理端列出公告 */
2603
+ export function useAdminAnnouncements(params?: any) {
2604
+ return useQuery({
2605
+ queryKey: ['notification-service', params] as const,
2606
+ queryFn: () => api.adminAnnouncements(params),
2607
+ });
2608
+ }
2609
+
2602
2610
  /** 管理员查看指定用户通知列表 */
2603
2611
  export function useAdminNotifications(params?: any) {
2604
2612
  return useQuery({
@@ -9153,6 +9161,17 @@ export function usePostNotificationsSend_from_template() {
9153
9161
  });
9154
9162
  }
9155
9163
 
9164
+ /** 签发 SSE 短期票据 */
9165
+ export function usePostNotificationsStream_ticket() {
9166
+ const qc = useQueryClient();
9167
+ return useMutation({
9168
+ mutationFn: () => api.notificationsStreamTicketPost(),
9169
+ onSuccess: () => {
9170
+ qc.invalidateQueries({ queryKey: ['notification-service'] });
9171
+ },
9172
+ });
9173
+ }
9174
+
9156
9175
  /** 发送测试通知 */
9157
9176
  export function usePostNotificationsTest() {
9158
9177
  const qc = useQueryClient();
@@ -1,6 +1,6 @@
1
1
  // Auto-generated from swagger.json annotations
2
2
  // DO NOT EDIT — run `python scripts/generate_api_ts.py` to regenerate
3
- // Generated: 2026-08-22 06:50:43
3
+ // Generated: 2026-09-30 17:04:31
4
4
 
5
5
  // ============================================================
6
6
  // Shared generic types
@@ -7516,6 +7516,71 @@ export type COSEAlgorithmIdentifier = "-7" | "-8" | "-9" | "-19" | "-35" | "-36"
7516
7516
  // notification-service
7517
7517
  // ============================================================
7518
7518
 
7519
+ export interface DataResponsegitee_com_autional_servicenotification_internal_handler_dto_EventMappingResponse {
7520
+ code?: number;
7521
+ data?: EventMappingResponse;
7522
+ message?: string;
7523
+ timestamp?: string;
7524
+ }
7525
+
7526
+ export interface DataResponsegitee_com_autional_servicenotification_internal_handler_dto_ExportUserDataResponse {
7527
+ code?: number;
7528
+ data?: ExportUserDataResponse;
7529
+ message?: string;
7530
+ timestamp?: string;
7531
+ }
7532
+
7533
+ export interface DataResponsegitee_com_autional_servicenotification_internal_handler_dto_NotificationPreferencesResponse {
7534
+ code?: number;
7535
+ data?: NotificationPreferencesResponse;
7536
+ message?: string;
7537
+ timestamp?: string;
7538
+ }
7539
+
7540
+ export interface DataResponsegitee_com_autional_servicenotification_internal_handler_dto_NotificationResponse {
7541
+ code?: number;
7542
+ data?: NotificationResponse;
7543
+ message?: string;
7544
+ timestamp?: string;
7545
+ }
7546
+
7547
+ export interface DataResponsegitee_com_autional_servicenotification_internal_handler_dto_StreamTicketResponse {
7548
+ code?: number;
7549
+ data?: StreamTicketResponse;
7550
+ message?: string;
7551
+ timestamp?: string;
7552
+ }
7553
+
7554
+ export interface DataResponsegitee_com_autional_servicenotification_internal_handler_dto_TemplateResponse {
7555
+ code?: number;
7556
+ data?: TemplateResponse;
7557
+ message?: string;
7558
+ timestamp?: string;
7559
+ }
7560
+
7561
+ export interface DataResponsehandler_HealthErrorResponse {
7562
+ code?: number;
7563
+ data?: HealthErrorResponse;
7564
+ message?: string;
7565
+ timestamp?: string;
7566
+ }
7567
+
7568
+ export interface DataResponsemap_string_string {
7569
+ code?: number;
7570
+ data?: Map_string_string;
7571
+ message?: string;
7572
+ timestamp?: string;
7573
+ }
7574
+
7575
+ export interface ListResponsegitee_com_autional_servicenotification_internal_handler_dto_NotificationResponse {
7576
+ code?: number;
7577
+ items?: NotificationResponse[]; // 统一使用 items
7578
+ message?: string;
7579
+ pagination?: PageInfo; // 分页信息(嵌套对象)
7580
+ timestamp?: string;
7581
+ total?: number; // 总条数(平铺,便于直接读取)
7582
+ }
7583
+
7519
7584
  export interface AnnouncementDetailResponse {
7520
7585
  code?: number;
7521
7586
  data?: AnnouncementResponse;
@@ -7641,48 +7706,6 @@ export interface CreateGlobalVariableRequest {
7641
7706
  value: string;
7642
7707
  }
7643
7708
 
7644
- export interface DataResponsedto_ExportUserDataResponse {
7645
- code?: number;
7646
- data?: ExportUserDataResponse;
7647
- message?: string;
7648
- timestamp?: string;
7649
- }
7650
-
7651
- export interface DataResponsedto_NotificationPreferencesResponse {
7652
- code?: number;
7653
- data?: NotificationPreferencesResponse;
7654
- message?: string;
7655
- timestamp?: string;
7656
- }
7657
-
7658
- export interface DataResponsedto_NotificationResponse {
7659
- code?: number;
7660
- data?: NotificationResponse;
7661
- message?: string;
7662
- timestamp?: string;
7663
- }
7664
-
7665
- export interface DataResponsedto_TemplateResponse {
7666
- code?: number;
7667
- data?: TemplateResponse;
7668
- message?: string;
7669
- timestamp?: string;
7670
- }
7671
-
7672
- export interface DataResponsehandler_HealthErrorResponse {
7673
- code?: number;
7674
- data?: HealthErrorResponse;
7675
- message?: string;
7676
- timestamp?: string;
7677
- }
7678
-
7679
- export interface DataResponsemap_string_string {
7680
- code?: number;
7681
- data?: Map_string_string;
7682
- message?: string;
7683
- timestamp?: string;
7684
- }
7685
-
7686
7709
  export interface DeleteNotificationDetailResponse {
7687
7710
  code?: number;
7688
7711
  data?: DeleteNotificationResponse;
@@ -7776,13 +7799,20 @@ export interface InternalPushSendRequest {
7776
7799
  userId?: string; // @example user-001
7777
7800
  }
7778
7801
 
7779
- export interface ListResponsedto_NotificationResponse {
7780
- code?: number;
7781
- items?: NotificationResponse[]; // 统一使用 items
7782
- message?: string;
7783
- pagination?: PageInfo; // 分页信息(嵌套对象)
7784
- timestamp?: string;
7785
- total?: number; // 总条数(平铺,便于直接读取)
7802
+ export interface InternalSendExternalRequest {
7803
+ channel: string; // @example email
7804
+ locale?: string; // @example en-US
7805
+ recipient: string; // @example user@example.com
7806
+ templateCode: string; // @example status_subscription_confirm
7807
+ variables?: Record<string, string>;
7808
+ }
7809
+
7810
+ export interface InternalUpsertEventMappingRequest {
7811
+ channel?: string; // 空 → in_app | @example in_app
7812
+ eventType: string; // @example user.registered
7813
+ isEnabled?: boolean; // 空 → true
7814
+ priority?: string; // 空 → medium | @example medium
7815
+ templateCode: string; // @example welcome
7786
7816
  }
7787
7817
 
7788
7818
  export interface MarkReadDetailResponse {
@@ -8008,6 +8038,12 @@ export interface SendNotificationRequest {
8008
8038
  userId: string; // 用户ID | @example usr_abc123
8009
8039
  }
8010
8040
 
8041
+ /** 一次性 SSE 票据:60s 内有效、消费一次即失效;以 ?ticket= 建立 EventSource 流 */
8042
+ export interface StreamTicketResponse {
8043
+ expiresIn?: number; // 有效期(秒) | @example 60
8044
+ ticket?: string; // 一次性票据 | @example 1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0u1V2
8045
+ }
8046
+
8011
8047
  export interface TestNotificationDetailResponse {
8012
8048
  code?: number;
8013
8049
  data?: TestNotificationResponse;
@@ -8873,6 +8909,13 @@ export interface DataResponsedomain_PointIntegrityResult {
8873
8909
  timestamp?: string;
8874
8910
  }
8875
8911
 
8912
+ export interface DataResponsedto_ExportUserDataResponse {
8913
+ code?: number;
8914
+ data?: ExportUserDataResponse;
8915
+ message?: string;
8916
+ timestamp?: string;
8917
+ }
8918
+
8876
8919
  export interface DeleteUserDataRequest {
8877
8920
  userId: string;
8878
8921
  }
package/src/vite-env.d.ts CHANGED
@@ -9,6 +9,7 @@ interface ImportMetaEnv {
9
9
  readonly VITE_OAUTH_CLIENT_ID: string;
10
10
  readonly VITE_AUTH_PAGES_URL: string;
11
11
  readonly VITE_API_BASE_URL: string;
12
+ readonly VITE_API_NAMESPACE: string;
12
13
  readonly VITE_AUDIT_BASE_URL: string;
13
14
  [key: string]: string | boolean | undefined;
14
15
  }