@polardbx/pxbaas 0.1.0-beta.2 → 0.1.0-beta.4

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.
Files changed (115) hide show
  1. package/README.md +116 -13
  2. package/dist/app/local-index.d.ts +70 -0
  3. package/dist/app/local-index.d.ts.map +1 -0
  4. package/dist/app/local-index.js +140 -0
  5. package/dist/app/local-index.js.map +1 -0
  6. package/dist/app/preflight.d.ts +24 -1
  7. package/dist/app/preflight.d.ts.map +1 -1
  8. package/dist/app/preflight.js +47 -11
  9. package/dist/app/preflight.js.map +1 -1
  10. package/dist/app/spec-lint.d.ts.map +1 -1
  11. package/dist/app/spec-lint.js +16 -0
  12. package/dist/app/spec-lint.js.map +1 -1
  13. package/dist/cli/root.d.ts.map +1 -1
  14. package/dist/cli/root.js +3 -0
  15. package/dist/cli/root.js.map +1 -1
  16. package/dist/commands/app/index.d.ts.map +1 -1
  17. package/dist/commands/app/index.js +428 -44
  18. package/dist/commands/app/index.js.map +1 -1
  19. package/dist/commands/completion/index.d.ts.map +1 -1
  20. package/dist/commands/completion/index.js +5 -1
  21. package/dist/commands/completion/index.js.map +1 -1
  22. package/dist/commands/data/index.d.ts +52 -0
  23. package/dist/commands/data/index.d.ts.map +1 -1
  24. package/dist/commands/data/index.js +201 -39
  25. package/dist/commands/data/index.js.map +1 -1
  26. package/dist/commands/db/index.d.ts +14 -0
  27. package/dist/commands/db/index.d.ts.map +1 -1
  28. package/dist/commands/db/index.js +151 -40
  29. package/dist/commands/db/index.js.map +1 -1
  30. package/dist/commands/functions/new.d.ts +4 -0
  31. package/dist/commands/functions/new.d.ts.map +1 -1
  32. package/dist/commands/functions/new.js +24 -9
  33. package/dist/commands/functions/new.js.map +1 -1
  34. package/dist/commands/gen/index.d.ts.map +1 -1
  35. package/dist/commands/gen/index.js +13 -10
  36. package/dist/commands/gen/index.js.map +1 -1
  37. package/dist/commands/login/index.d.ts.map +1 -1
  38. package/dist/commands/login/index.js +15 -30
  39. package/dist/commands/login/index.js.map +1 -1
  40. package/dist/commands/profile/import-env.d.ts +7 -2
  41. package/dist/commands/profile/import-env.d.ts.map +1 -1
  42. package/dist/commands/profile/import-env.js +141 -16
  43. package/dist/commands/profile/import-env.js.map +1 -1
  44. package/dist/commands/profile/index.d.ts.map +1 -1
  45. package/dist/commands/profile/index.js +13 -1
  46. package/dist/commands/profile/index.js.map +1 -1
  47. package/dist/commands/storage/index.d.ts.map +1 -1
  48. package/dist/commands/storage/index.js +209 -7
  49. package/dist/commands/storage/index.js.map +1 -1
  50. package/dist/commands/tenant/index.d.ts +19 -0
  51. package/dist/commands/tenant/index.d.ts.map +1 -0
  52. package/dist/commands/tenant/index.js +662 -0
  53. package/dist/commands/tenant/index.js.map +1 -0
  54. package/dist/commands/whoami/index.js +19 -0
  55. package/dist/commands/whoami/index.js.map +1 -1
  56. package/dist/credentials/credential-store.d.ts +30 -1
  57. package/dist/credentials/credential-store.d.ts.map +1 -1
  58. package/dist/credentials/credential-store.js +114 -12
  59. package/dist/credentials/credential-store.js.map +1 -1
  60. package/dist/credentials/env-file.d.ts +48 -7
  61. package/dist/credentials/env-file.d.ts.map +1 -1
  62. package/dist/credentials/env-file.js +91 -16
  63. package/dist/credentials/env-file.js.map +1 -1
  64. package/dist/credentials/jwt-introspect.d.ts +7 -0
  65. package/dist/credentials/jwt-introspect.d.ts.map +1 -1
  66. package/dist/credentials/jwt-introspect.js.map +1 -1
  67. package/dist/credentials/masker.d.ts +3 -0
  68. package/dist/credentials/masker.d.ts.map +1 -1
  69. package/dist/credentials/masker.js +3 -0
  70. package/dist/credentials/masker.js.map +1 -1
  71. package/dist/credentials/profile.d.ts +12 -0
  72. package/dist/credentials/profile.d.ts.map +1 -1
  73. package/dist/credentials/profile.js +23 -0
  74. package/dist/credentials/profile.js.map +1 -1
  75. package/dist/credentials/tty-guard.d.ts +10 -0
  76. package/dist/credentials/tty-guard.d.ts.map +1 -1
  77. package/dist/credentials/tty-guard.js +32 -0
  78. package/dist/credentials/tty-guard.js.map +1 -1
  79. package/dist/db/introspect.d.ts +54 -0
  80. package/dist/db/introspect.d.ts.map +1 -0
  81. package/dist/db/introspect.js +140 -0
  82. package/dist/db/introspect.js.map +1 -0
  83. package/dist/main.js +21 -2
  84. package/dist/main.js.map +1 -1
  85. package/dist/tenant/bundle.d.ts +66 -0
  86. package/dist/tenant/bundle.d.ts.map +1 -0
  87. package/dist/tenant/bundle.js +132 -0
  88. package/dist/tenant/bundle.js.map +1 -0
  89. package/dist/tenant/guide.d.ts +50 -0
  90. package/dist/tenant/guide.d.ts.map +1 -0
  91. package/dist/tenant/guide.js +152 -0
  92. package/dist/tenant/guide.js.map +1 -0
  93. package/dist/tenant/probe.d.ts +65 -0
  94. package/dist/tenant/probe.d.ts.map +1 -0
  95. package/dist/tenant/probe.js +159 -0
  96. package/dist/tenant/probe.js.map +1 -0
  97. package/dist/tenant/verify.d.ts +37 -0
  98. package/dist/tenant/verify.d.ts.map +1 -0
  99. package/dist/tenant/verify.js +132 -0
  100. package/dist/tenant/verify.js.map +1 -0
  101. package/dist/version.d.ts +29 -0
  102. package/dist/version.d.ts.map +1 -0
  103. package/dist/version.js +65 -0
  104. package/dist/version.js.map +1 -0
  105. package/package.json +2 -2
  106. package/skills/pxbaas-app-spec/references/runtime-contract.md +2 -2
  107. package/skills/pxbaas-cli/SKILL.md +14 -5
  108. package/skills/pxbaas-cli/references/auth-and-credentials.md +19 -3
  109. package/skills/pxbaas-cli/references/data-postgrest.md +19 -3
  110. package/skills/pxbaas-cli/references/db-and-meta.md +5 -0
  111. package/skills/pxbaas-cli/references/safety-and-two-phase.md +42 -13
  112. package/skills/pxbaas-cli/references/tenant-admin.md +122 -0
  113. package/skills/pxbaas-cli/references/troubleshooting.md +4 -1
  114. package/templates/webapp/react-vite-hono/backend/index.ts +21 -4
  115. package/templates/webapp/ssr-hono/backend/index.ts +50 -31
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.js","sourceRoot":"","sources":["../../src/db/introspect.ts"],"names":[],"mappings":"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"}
package/dist/main.js CHANGED
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { program } from 'commander';
3
3
  import { registerCommands } from './cli/root.js';
4
- import { ProfileManager, setCliProfileOverrideGlobal } from './credentials/profile.js';
4
+ import { ProfileManager, isProfileCreatingCommand, setCliProfileOverrideGlobal, } from './credentials/profile.js';
5
+ import { resolvePackageVersion } from './version.js';
5
6
  // Global options
6
7
  program
7
8
  .name('pxbaas')
8
9
  .description('PolarDB-X BaaS CLI')
9
- .version('0.0.0-dev')
10
+ // Read from package.json at runtime: a hardcoded string here reported
11
+ // `0.0.0-dev` for every build, so `--version` could never identify a release.
12
+ .version(resolvePackageVersion())
10
13
  .option('--json', 'Output in JSON format')
11
14
  .option('--profile <name>', 'Use specific profile')
12
15
  .option('--debug', 'Enable debug output');
@@ -25,6 +28,15 @@ const declareProfileOption = (cmd) => {
25
28
  cmd.commands.forEach(declareProfileOption);
26
29
  };
27
30
  declareProfileOption(program);
31
+ /** Full command path of the action command (e.g. ['profile','import']), program name excluded. */
32
+ const commandPathOf = (actionCommand) => {
33
+ const parts = [];
34
+ for (let c = actionCommand; c && c !== program; c = c.parent) {
35
+ if (c.name())
36
+ parts.unshift(c.name());
37
+ }
38
+ return parts;
39
+ };
28
40
  // Resolve --profile once, before any command runs: validate it exists (fail loudly
29
41
  // instead of silently falling back to the current profile) and publish it as a
30
42
  // process-wide override. Never persisted — `profile ls` current marker is untouched.
@@ -32,6 +44,13 @@ program.hook('preAction', async (_thisCommand, actionCommand) => {
32
44
  const requested = (actionCommand.opts().profile ?? program.opts().profile);
33
45
  if (!requested)
34
46
  return;
47
+ // `login` / `profile import` write the named profile, so an unknown name is their
48
+ // precondition, not an error. Still publish the override: the command must read and
49
+ // write that exact profile rather than whatever happens to be current.
50
+ if (isProfileCreatingCommand(commandPathOf(actionCommand))) {
51
+ setCliProfileOverrideGlobal(requested);
52
+ return;
53
+ }
35
54
  const manager = new ProfileManager();
36
55
  const known = await manager.listProfiles();
37
56
  if (!known.some((p) => p.name === requested)) {
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAW,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvF,iBAAiB;AACjB,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,oBAAoB,CAAC;KACjC,OAAO,CAAC,WAAW,CAAC;KACpB,MAAM,CAAC,QAAQ,EAAE,uBAAuB,CAAC;KACzC,MAAM,CAAC,kBAAkB,EAAE,sBAAsB,CAAC;KAClD,MAAM,CAAC,SAAS,EAAE,qBAAqB,CAAC,CAAC;AAE5C,wBAAwB;AACxB,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAE1B,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,sEAAsE;AACtE,mEAAmE;AACnE,MAAM,oBAAoB,GAAG,CAAC,GAAY,EAAQ,EAAE;IAClD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,CAAC;QACrD,GAAG,CAAC,MAAM,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IACzD,CAAC;IACD,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAC7C,CAAC,CAAC;AACF,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAE9B,mFAAmF;AACnF,+EAA+E;AAC/E,qFAAqF;AACrF,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,EAAE;IAC9D,MAAM,SAAS,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAE5D,CAAC;IACd,IAAI,CAAC,SAAS;QAAE,OAAO;IACvB,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;IAC3C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;QAC9D,OAAO,CAAC,KAAK,CACX,uBAAuB,SAAS,0BAA0B,KAAK,EAAE,CAClE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,2BAA2B,CAAC,SAAS,CAAC,CAAC;AACzC,CAAC,CAAC,CAAC;AAEH,kBAAkB;AAClB,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAW,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD,iBAAiB;AACjB,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,oBAAoB,CAAC;IAClC,sEAAsE;IACtE,8EAA8E;KAC7E,OAAO,CAAC,qBAAqB,EAAE,CAAC;KAChC,MAAM,CAAC,QAAQ,EAAE,uBAAuB,CAAC;KACzC,MAAM,CAAC,kBAAkB,EAAE,sBAAsB,CAAC;KAClD,MAAM,CAAC,SAAS,EAAE,qBAAqB,CAAC,CAAC;AAE5C,wBAAwB;AACxB,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAE1B,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,sEAAsE;AACtE,mEAAmE;AACnE,MAAM,oBAAoB,GAAG,CAAC,GAAY,EAAQ,EAAE;IAClD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,CAAC;QACrD,GAAG,CAAC,MAAM,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IACzD,CAAC;IACD,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAC7C,CAAC,CAAC;AACF,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAE9B,kGAAkG;AAClG,MAAM,aAAa,GAAG,CAAC,aAAsB,EAAY,EAAE;IACzD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAmB,aAAa,EAAE,CAAC,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;QAC7E,IAAI,CAAC,CAAC,IAAI,EAAE;YAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,mFAAmF;AACnF,+EAA+E;AAC/E,qFAAqF;AACrF,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,EAAE;IAC9D,MAAM,SAAS,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAE5D,CAAC;IACd,IAAI,CAAC,SAAS;QAAE,OAAO;IACvB,kFAAkF;IAClF,oFAAoF;IACpF,uEAAuE;IACvE,IAAI,wBAAwB,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC;QAC3D,2BAA2B,CAAC,SAAS,CAAC,CAAC;QACvC,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;IAC3C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;QAC9D,OAAO,CAAC,KAAK,CACX,uBAAuB,SAAS,0BAA0B,KAAK,EAAE,CAClE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,2BAA2B,CAAC,SAAS,CAAC,CAAC;AACzC,CAAC,CAAC,CAAC;AAEH,kBAAkB;AAClB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * 租户交付包(tenant access bundle)。
3
+ *
4
+ * 解决的问题:管理员在 CLI 里拿到了新租户的密钥之后,怎么把它交给"另一个真人"。
5
+ * 交付物是一份**规范 env 文件**,格式与 `pxbaas profile import` 的多租户输入契约逐字
6
+ * 对齐(credentials/env-file.ts 的 TENANT_* 分支),所以收件人不需要手改任何字段名。
7
+ *
8
+ * 三条硬纪律:
9
+ * 1. **字段白名单**。bundle 内容由本模块**逐字段显式拼装**,因此结构上不可能包含
10
+ * `jwt_secret`(租户签名母密钥)、`DASHBOARD_*`(实例管理口令)或 `GLOBAL_*`
11
+ * (平台全局密钥)。这不是"记得过滤",而是根本没有把它们写进去的入口。
12
+ * 2. **明文只落 0600 文件,绝不进 stdout / `--json` / 日志**。终端上只出现指纹。
13
+ * 3. **不留存于内存之外**:调用方拿到 keys 后要么写钥匙串,要么交给本模块写文件。
14
+ */
15
+ export declare const BUNDLE_VERSION = 1;
16
+ export interface BundleInput {
17
+ tenantId: string;
18
+ /** Kong 网关地址;必须已是网关,不能是 Studio 端口。 */
19
+ endpoint: string;
20
+ anonKey: string;
21
+ serviceRoleKey: string;
22
+ /** 同 VPC 使用者可加内网入口,导入时用 --intranet 优先。 */
23
+ intranetEndpoint?: string;
24
+ issuedBy?: string;
25
+ /** 供注释头标注有效期语义(当前平台签发约 10 年且不可撤销)。 */
26
+ generatedAt?: string;
27
+ }
28
+ export interface HandoffRecord {
29
+ tenantId: string;
30
+ endpoint: string;
31
+ generatedAt: string;
32
+ anonFingerprint: string;
33
+ serviceFingerprint: string;
34
+ bundlePath: string;
35
+ /** 交付包整文件 sha256,用于收件人比对"拿到的是不是同一份"。 */
36
+ bundleSha256: string;
37
+ issuedBy?: string;
38
+ }
39
+ export interface WriteBundleResult {
40
+ path: string;
41
+ bytes: number;
42
+ record: HandoffRecord;
43
+ instructions: string;
44
+ }
45
+ /**
46
+ * 渲染交付包文本。只输出这 5(+1)个变量:
47
+ * MULTI_TENANT_MODE / SUPABASE_URL / DEFAULT_TENANT_ID / TENANT_ANON_KEY /
48
+ * TENANT_SERVICE_ROLE_KEY(可选 SUPABASE_INTRANET_URL)。
49
+ */
50
+ export declare function renderBundle(input: BundleInput): string;
51
+ /**
52
+ * 写交付包。目标已存在时**必须**显式 `overwrite`,否则拒绝 —— 不静默覆盖别人的接入包。
53
+ */
54
+ export declare function writeBundle(file: string, input: BundleInput, options?: {
55
+ overwrite?: boolean;
56
+ homeDir?: string;
57
+ }): Promise<WriteBundleResult>;
58
+ /** 可直接复制转发给使用者的交接说明(不含任何密钥值)。 */
59
+ export declare function renderInstructions(record: HandoffRecord): string;
60
+ export declare function listHandoffs(homeDir?: string): Promise<HandoffRecord[]>;
61
+ /**
62
+ * 签发台账:只记指纹与文件哈希,**不含任何明文密钥**。0600。
63
+ * 泄露事件回溯时这是唯一能回答"我发过哪些包、对应哪个租户"的记录。
64
+ */
65
+ export declare function appendHandoffRecord(record: HandoffRecord, homeDir?: string): Promise<void>;
66
+ //# sourceMappingURL=bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/tenant/bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,aAAa,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CA0BvD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,OAAO,CAAC,iBAAiB,CAAC,CA+B5B;AAED,iCAAiC;AACjC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAgBhE;AAID,wBAAsB,YAAY,CAChC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,EAAE,CAAC,CAY1B;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * 租户交付包(tenant access bundle)。
3
+ *
4
+ * 解决的问题:管理员在 CLI 里拿到了新租户的密钥之后,怎么把它交给"另一个真人"。
5
+ * 交付物是一份**规范 env 文件**,格式与 `pxbaas profile import` 的多租户输入契约逐字
6
+ * 对齐(credentials/env-file.ts 的 TENANT_* 分支),所以收件人不需要手改任何字段名。
7
+ *
8
+ * 三条硬纪律:
9
+ * 1. **字段白名单**。bundle 内容由本模块**逐字段显式拼装**,因此结构上不可能包含
10
+ * `jwt_secret`(租户签名母密钥)、`DASHBOARD_*`(实例管理口令)或 `GLOBAL_*`
11
+ * (平台全局密钥)。这不是"记得过滤",而是根本没有把它们写进去的入口。
12
+ * 2. **明文只落 0600 文件,绝不进 stdout / `--json` / 日志**。终端上只出现指纹。
13
+ * 3. **不留存于内存之外**:调用方拿到 keys 后要么写钥匙串,要么交给本模块写文件。
14
+ */
15
+ import { createHash } from 'node:crypto';
16
+ import { existsSync } from 'node:fs';
17
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
18
+ import { dirname, join, resolve } from 'node:path';
19
+ import { homedir } from 'node:os';
20
+ import { computeFingerprint } from '../credentials/index.js';
21
+ export const BUNDLE_VERSION = 1;
22
+ /**
23
+ * 渲染交付包文本。只输出这 5(+1)个变量:
24
+ * MULTI_TENANT_MODE / SUPABASE_URL / DEFAULT_TENANT_ID / TENANT_ANON_KEY /
25
+ * TENANT_SERVICE_ROLE_KEY(可选 SUPABASE_INTRANET_URL)。
26
+ */
27
+ export function renderBundle(input) {
28
+ const endpoint = stripSlash(input.endpoint);
29
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
30
+ // 注释头:parseEnvText 会安全忽略 '#' 行,因此头部不需要解析器改动。
31
+ const header = [
32
+ '# PolarDB-X BaaS 租户接入包 (tenant access bundle)',
33
+ `# tenant_id : ${input.tenantId}`,
34
+ `# instance : ${endpoint} (Kong 网关,勿改成 Studio/内网调试端口)`,
35
+ `# created_at : ${generatedAt}`,
36
+ input.issuedBy ? `# issued_by : ${input.issuedBy}` : undefined,
37
+ '#',
38
+ '# ⚠ 本文件含明文租户密钥,且:有效期约 10 年、当前平台不支持撤销。',
39
+ '# - 只通过安全通道传输(受控网盘 / 密邮正文 / 当面拷贝);不要贴进 IM 群、工单附件、截图或聊天记录',
40
+ '# - 收件人导入成功后请删除本文件(凭据会进入其操作系统钥匙串)',
41
+ '# - 怀疑泄露:当前只能由平台侧重建该租户',
42
+ '# ============================================================',
43
+ 'MULTI_TENANT_MODE=true',
44
+ `SUPABASE_URL=${endpoint}`,
45
+ `DEFAULT_TENANT_ID=${input.tenantId}`,
46
+ `TENANT_ANON_KEY=${input.anonKey}`,
47
+ `TENANT_SERVICE_ROLE_KEY=${input.serviceRoleKey}`,
48
+ ];
49
+ if (input.intranetEndpoint) {
50
+ header.push(`SUPABASE_INTRANET_URL=${stripSlash(input.intranetEndpoint)}`);
51
+ }
52
+ return header.join('\n') + '\n';
53
+ }
54
+ /**
55
+ * 写交付包。目标已存在时**必须**显式 `overwrite`,否则拒绝 —— 不静默覆盖别人的接入包。
56
+ */
57
+ export async function writeBundle(file, input, options = {}) {
58
+ const path = resolve(file);
59
+ if (existsSync(path) && !options.overwrite) {
60
+ throw new Error(`Bundle file already exists: ${path}. Pass --overwrite to replace it (the old bundle's keys stay valid — overwriting the file does NOT revoke them).`);
61
+ }
62
+ const text = renderBundle(input);
63
+ const dir = dirname(path);
64
+ if (!existsSync(dir)) {
65
+ await mkdir(dir, { mode: 0o700, recursive: true });
66
+ }
67
+ await writeFile(path, text, { mode: 0o600 });
68
+ await chmod(path, 0o600);
69
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
70
+ const record = {
71
+ tenantId: input.tenantId,
72
+ endpoint: stripSlash(input.endpoint),
73
+ generatedAt,
74
+ anonFingerprint: computeFingerprint(input.anonKey),
75
+ serviceFingerprint: computeFingerprint(input.serviceRoleKey),
76
+ bundlePath: path,
77
+ bundleSha256: createHash('sha256').update(text).digest('hex'),
78
+ issuedBy: input.issuedBy,
79
+ };
80
+ await appendHandoffRecord(record, options.homeDir);
81
+ return { path, bytes: Buffer.byteLength(text), record, instructions: renderInstructions(record) };
82
+ }
83
+ /** 可直接复制转发给使用者的交接说明(不含任何密钥值)。 */
84
+ export function renderInstructions(record) {
85
+ return [
86
+ `下一步:把 ${record.bundlePath} 通过安全通道发给该租户的使用者,并附上以下说明。`,
87
+ '',
88
+ ` 1) 安装 CLI(需 Node.js >= 20): npm install -g @polardbx/pxbaas@beta`,
89
+ ` 2) 导入接入包: pxbaas profile import ${record.bundlePath.split('/').pop()} --name ${record.tenantId} --json`,
90
+ ' 3) 自检: pxbaas whoami --json → 期望 mode=multi-tenant、tenantId=' + record.tenantId,
91
+ ' 4) 健康检查: pxbaas doctor --json → 5 项全 pass,Tenant Consistency 必须 pass',
92
+ ' 5) 打通数据面: pxbaas db tables --json',
93
+ ' 6) 全部通过后删除该 env 文件',
94
+ '',
95
+ ' 指纹核对(证明收件人拿到的就是这份包,防传输途中被替换):',
96
+ ` anon: ${record.anonFingerprint}`,
97
+ ` service_role: ${record.serviceFingerprint}`,
98
+ ' 收件人在 `pxbaas whoami --json` 里比对 anonKey.fingerprint / serviceKey.fingerprint 是否一致。',
99
+ ].join('\n');
100
+ }
101
+ const HANDOFF_FILE_NAME = 'handoffs.json';
102
+ export async function listHandoffs(homeDir) {
103
+ const path = join(homeDir ?? join(homedir(), '.baas'), HANDOFF_FILE_NAME);
104
+ if (!existsSync(path))
105
+ return [];
106
+ try {
107
+ const parsed = JSON.parse(await readFile(path, 'utf-8'));
108
+ return parsed.records ?? [];
109
+ }
110
+ catch {
111
+ return [];
112
+ }
113
+ }
114
+ /**
115
+ * 签发台账:只记指纹与文件哈希,**不含任何明文密钥**。0600。
116
+ * 泄露事件回溯时这是唯一能回答"我发过哪些包、对应哪个租户"的记录。
117
+ */
118
+ export async function appendHandoffRecord(record, homeDir) {
119
+ const dir = homeDir ?? join(homedir(), '.baas');
120
+ const path = join(dir, HANDOFF_FILE_NAME);
121
+ const records = await listHandoffs(dir);
122
+ records.push(record);
123
+ if (!existsSync(dir)) {
124
+ await mkdir(dir, { mode: 0o700, recursive: true });
125
+ }
126
+ await writeFile(path, JSON.stringify({ version: BUNDLE_VERSION, records }, null, 2) + '\n', { mode: 0o600 });
127
+ await chmod(path, 0o600);
128
+ }
129
+ function stripSlash(value) {
130
+ return value.replace(/\/+$/, '');
131
+ }
132
+ //# sourceMappingURL=bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/tenant/bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAkChC;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAClE,6CAA6C;IAC7C,MAAM,MAAM,GAAG;QACb,+CAA+C;QAC/C,kBAAkB,KAAK,CAAC,QAAQ,EAAE;QAClC,kBAAkB,QAAQ,gCAAgC;QAC1D,kBAAkB,WAAW,EAAE;QAC/B,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,kBAAkB,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS;QAC/D,GAAG;QACH,uCAAuC;QACvC,4DAA4D;QAC5D,qCAAqC;QACrC,0BAA0B;QAC1B,gEAAgE;QAChE,wBAAwB;QACxB,gBAAgB,QAAQ,EAAE;QAC1B,qBAAqB,KAAK,CAAC,QAAQ,EAAE;QACrC,mBAAmB,KAAK,CAAC,OAAO,EAAE;QAClC,2BAA2B,KAAK,CAAC,cAAc,EAAE;KAClD,CAAC;IACF,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,yBAAyB,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAY,EACZ,KAAkB,EAClB,UAAqD,EAAE;IAEvD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,+BAA+B,IAAI,kHAAkH,CACtJ,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7C,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAEzB,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAClE,MAAM,MAAM,GAAkB;QAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;QACpC,WAAW;QACX,eAAe,EAAE,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC;QAClD,kBAAkB,EAAE,kBAAkB,CAAC,KAAK,CAAC,cAAc,CAAC;QAC5D,UAAU,EAAE,IAAI;QAChB,YAAY,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC7D,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC;IAEF,MAAM,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AACpG,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,kBAAkB,CAAC,MAAqB;IACtD,OAAO;QACL,SAAS,MAAM,CAAC,UAAU,2BAA2B;QACrD,EAAE;QACF,oEAAoE;QACpE,qDAAqD,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,WAAW,MAAM,CAAC,QAAQ,SAAS;QAC1H,wFAAwF,GAAG,MAAM,CAAC,QAAQ;QAC1G,4FAA4F;QAC5F,qDAAqD;QACrD,sBAAsB;QACtB,EAAE;QACF,iCAAiC;QACjC,qBAAqB,MAAM,CAAC,eAAe,EAAE;QAC7C,qBAAqB,MAAM,CAAC,kBAAkB,EAAE;QAChD,wFAAwF;KACzF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAgB;IAEhB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAC1E,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAGtD,CAAC;QACF,OAAO,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAqB,EACrB,OAAgB;IAEhB,MAAM,GAAG,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IACxC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,SAAS,CACb,IAAI,EACJ,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EACpE,EAAE,IAAI,EAAE,KAAK,EAAE,CAChB,CAAC;IACF,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 多租户接入缺口的诊断与引导(方案文档 §5.4)。
3
+ *
4
+ * 原来 `profile import` 撞到"多租户 env 却没有租户级凭据"时只抛一句拒绝,而它给出的
5
+ * 两个建议("重新生成 client.env"、"用 per-tenant key 登录")**都是使用者自己做不到**
6
+ * 的动作——租户级密钥由平台签发。结果是 CLI 把用户堵在门口,还让他以为是自己的错。
7
+ *
8
+ * 本模块把这一处变成两条明确出口:
9
+ * - 非管理员 → 一份可以直接转给实例管理员的**申请清单**(要哪个租户、哪两把值、
10
+ * 以及怎么判断拿到的到底是不是租户级密钥);
11
+ * - 具备管理面能力 → 指出可以用 `pxbaas tenant ...` 自助开通/取键,并给出命令。
12
+ *
13
+ * 边界:这里**只读、只打印**,绝不在失败分支里创建租户(读路径不携带建库/建账号/
14
+ * 占配额这类写副作用,见方案 N2)。管理凭据也只做**临时**探测,绝不落库(N3)。
15
+ */
16
+ import { type ParsedEnv } from '../credentials/index.js';
17
+ import { type TenantProbeResult } from './probe.js';
18
+ export interface KeyFinding {
19
+ variable: string;
20
+ role?: string;
21
+ /** JWT 里的 tenant_id claim;无 = 平台全局密钥,无法访问租户资源。 */
22
+ tenantId?: string;
23
+ expired: boolean;
24
+ fingerprint: string;
25
+ note: string;
26
+ }
27
+ export interface TenantGapDiagnostics {
28
+ envFile?: string;
29
+ endpoint?: string;
30
+ multiTenantMode?: string;
31
+ defaultTenantId?: string;
32
+ tenantLevelKeysPresent: string[];
33
+ keyFindings: KeyFinding[];
34
+ /** env 文件里是否有成对的 dashboard 凭据(仅报告存在,不导入)。 */
35
+ adminCredentialsPresent: boolean;
36
+ }
37
+ /** 纯本地解析:不联网、不写任何状态。 */
38
+ export declare function diagnoseTenantCredentialGap(env: ParsedEnv, options?: {
39
+ envFile?: string;
40
+ }): TenantGapDiagnostics;
41
+ /** 非管理员出口:一份可原样转给实例管理员的申请清单。 */
42
+ export declare function renderRequestChecklist(diag: TenantGapDiagnostics): string;
43
+ /** 管理员出口:指出可以自助的动作与命令。 */
44
+ export declare function renderAdminPath(diag: TenantGapDiagnostics, probe: TenantProbeResult): string;
45
+ /**
46
+ * 用 env 里的 dashboard 凭据做一次**临时**探测:只在内存里使用,不落库、不写文件。
47
+ * 没有成对凭据时返回 undefined(调用方走非管理员出口)。
48
+ */
49
+ export declare function tryProbeFromEnv(env: ParsedEnv, endpoint: string | undefined): Promise<TenantProbeResult | undefined>;
50
+ //# sourceMappingURL=guide.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guide.d.ts","sourceRoot":"","sources":["../../src/tenant/guide.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAExF,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AActE,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,6CAA6C;IAC7C,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAED,wBAAwB;AACxB,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,SAAS,EACd,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACjC,oBAAoB,CAiDtB;AAED,gCAAgC;AAChC,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAiCzE;AAED,0BAA0B;AAC1B,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAwB5F;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAUxC"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * 多租户接入缺口的诊断与引导(方案文档 §5.4)。
3
+ *
4
+ * 原来 `profile import` 撞到"多租户 env 却没有租户级凭据"时只抛一句拒绝,而它给出的
5
+ * 两个建议("重新生成 client.env"、"用 per-tenant key 登录")**都是使用者自己做不到**
6
+ * 的动作——租户级密钥由平台签发。结果是 CLI 把用户堵在门口,还让他以为是自己的错。
7
+ *
8
+ * 本模块把这一处变成两条明确出口:
9
+ * - 非管理员 → 一份可以直接转给实例管理员的**申请清单**(要哪个租户、哪两把值、
10
+ * 以及怎么判断拿到的到底是不是租户级密钥);
11
+ * - 具备管理面能力 → 指出可以用 `pxbaas tenant ...` 自助开通/取键,并给出命令。
12
+ *
13
+ * 边界:这里**只读、只打印**,绝不在失败分支里创建租户(读路径不携带建库/建账号/
14
+ * 占配额这类写副作用,见方案 N2)。管理凭据也只做**临时**探测,绝不落库(N3)。
15
+ */
16
+ import { computeFingerprint, decodeJwt } from '../credentials/index.js';
17
+ import { resolveAdminFromEnv } from '../credentials/env-file.js';
18
+ import { probeTenantAdmin } from './probe.js';
19
+ /** 判据用的候选变量:先看租户级,再看会被误当租户级的平台全局级。 */
20
+ const KEY_CANDIDATES = [
21
+ 'TENANT_ANON_KEY',
22
+ 'TENANT_SERVICE_ROLE_KEY',
23
+ 'SUPABASE_ANON_KEY',
24
+ 'ANON_KEY',
25
+ 'GLOBAL_ANON_KEY',
26
+ 'SUPABASE_SERVICE_ROLE_KEY',
27
+ 'SERVICE_ROLE_KEY',
28
+ 'GLOBAL_SERVICE_ROLE_KEY',
29
+ ];
30
+ /** 纯本地解析:不联网、不写任何状态。 */
31
+ export function diagnoseTenantCredentialGap(env, options = {}) {
32
+ const keyFindings = [];
33
+ const tenantLevelKeysPresent = [];
34
+ for (const variable of KEY_CANDIDATES) {
35
+ const value = (env[variable] ?? '').trim();
36
+ if (!value)
37
+ continue;
38
+ const parts = value.split('.');
39
+ // 与 whoami / tenant 同一套展示口径:sha256 前 8 + 后 4,前缀只在这一处加。
40
+ const fingerprint = computeFingerprint(value);
41
+ let role;
42
+ let tenantId;
43
+ let expired = false;
44
+ let note = 'Value is not a JWT (three dot-separated parts expected) — cannot be a Supabase-style API key.';
45
+ if (parts.length === 3) {
46
+ try {
47
+ const payload = decodeJwt(value);
48
+ role = payload.role;
49
+ tenantId = payload.tenant_id;
50
+ expired = typeof payload.exp === 'number' && payload.exp * 1000 <= Date.now();
51
+ note = tenantId
52
+ ? `Tenant-level key for tenant "${tenantId}"${expired ? ' (EXPIRED)' : ''}`
53
+ : `Platform-global key (no tenant_id claim)${expired ? ' (EXPIRED)' : ''} — cannot reach tenant resources`;
54
+ }
55
+ catch (error) {
56
+ note = `JWT payload unparsable: ${error instanceof Error ? error.message : String(error)}`;
57
+ }
58
+ }
59
+ if (/^TENANT_/.test(variable))
60
+ tenantLevelKeysPresent.push(variable);
61
+ keyFindings.push({ variable, role, tenantId, expired, fingerprint, note });
62
+ }
63
+ const endpoint = firstNonEmpty([
64
+ env.SUPABASE_URL,
65
+ env.SUPABASE_PUBLIC_URL,
66
+ env.SUPABASE_INTRANET_URL,
67
+ ]);
68
+ return {
69
+ envFile: options.envFile,
70
+ endpoint,
71
+ multiTenantMode: firstNonEmpty([env.MULTI_TENANT_MODE]),
72
+ defaultTenantId: firstNonEmpty([env.DEFAULT_TENANT_ID]),
73
+ tenantLevelKeysPresent,
74
+ keyFindings,
75
+ adminCredentialsPresent: Boolean(resolveAdminFromEnv(env)),
76
+ };
77
+ }
78
+ /** 非管理员出口:一份可原样转给实例管理员的申请清单。 */
79
+ export function renderRequestChecklist(diag) {
80
+ const lines = [
81
+ '你需要的不是新建租户,而是**某个租户的两把 per-tenant 密钥**。请向实例管理员申请:',
82
+ '',
83
+ ` tenant_id: ${diag.defaultTenantId ?? '<要访问的租户 ID>'}`,
84
+ ' anon key: (JWT payload 必须含 tenant_id)',
85
+ ' service role key: (同上)',
86
+ ` 实例端点: ${diag.endpoint ?? '<Kong 网关地址,通常是 :8000>'}`,
87
+ '',
88
+ '拿到后请写成这样一份接入包(变量名必须逐字一致),再导入:',
89
+ '',
90
+ ' MULTI_TENANT_MODE=true',
91
+ ` SUPABASE_URL=${diag.endpoint ?? 'https://<网关地址>'}`,
92
+ ` DEFAULT_TENANT_ID=${diag.defaultTenantId ?? '<租户 ID>'}`,
93
+ ' TENANT_ANON_KEY=<粘贴 anon key>',
94
+ ' TENANT_SERVICE_ROLE_KEY=<粘贴 service role key>',
95
+ '',
96
+ ` pxbaas profile import ./<租户>.pxbaas.env --name <租户> --json`,
97
+ ' pxbaas whoami --json # 期望 mode=multi-tenant 且 tenantId 正确',
98
+ '',
99
+ '自检判据(拿到密钥先解码看一眼,能立刻分辨管理员给的是不是租户级密钥):',
100
+ " printf '%s' \"$TENANT_ANON_KEY\" | cut -d. -f2 | base64 -d",
101
+ ' → payload 里必须出现 "tenant_id":"<租户ID>";没有就是平台全局密钥,用不了。',
102
+ ];
103
+ if (diag.adminCredentialsPresent) {
104
+ lines.push('', '注意:这份 env 里带着 DASHBOARD_USERNAME / DASHBOARD_PASSWORD(实例管理面口令)。', '按安全设计它们不会被导入。若这份文件原本来自管理员,请改由管理员用', '"pxbaas tenant login" + "pxbaas tenant keys <id> --bundle <file>" 正规发放接入包。');
105
+ }
106
+ return lines.join('\n');
107
+ }
108
+ /** 管理员出口:指出可以自助的动作与命令。 */
109
+ export function renderAdminPath(diag, probe) {
110
+ const endpoint = diag.endpoint ?? probe.endpoint;
111
+ const tenant = diag.defaultTenantId;
112
+ const lines = [
113
+ '本机凭据可打开该实例的租户管理面。可以选择自助处理(CLI 不会替你建租户,需显式执行):',
114
+ '',
115
+ ` pxbaas tenant login --endpoint ${endpoint} # 绑定管理凭据(口令走掩码或 stdin,不进 argv)`,
116
+ ' pxbaas tenant ls --json # 看这个实例上到底有哪些租户',
117
+ ];
118
+ if (tenant) {
119
+ lines.push(` pxbaas tenant keys ${tenant} --admin --as-profile # 已有该租户:直接把它的密钥绑进本机钥匙串`, ` pxbaas tenant keys ${tenant} --admin --bundle ./${tenant}.pxbaas.env # 要交给别人:生成规范接入包`, ` pxbaas tenant create ${tenant} --dry-run # 确实需要新租户时先看计划`);
120
+ }
121
+ else {
122
+ lines.push(' pxbaas tenant create <tenant-id> --dry-run # 需要新租户时先看计划');
123
+ }
124
+ lines.push('', ' 提示:先跑 tenant ls 再决定建不建——多数情况该做的是取回已有租户的密钥,', ' 而不是造一个新租户。');
125
+ return lines.join('\n');
126
+ }
127
+ /**
128
+ * 用 env 里的 dashboard 凭据做一次**临时**探测:只在内存里使用,不落库、不写文件。
129
+ * 没有成对凭据时返回 undefined(调用方走非管理员出口)。
130
+ */
131
+ export async function tryProbeFromEnv(env, endpoint) {
132
+ if (!endpoint)
133
+ return undefined;
134
+ const admin = resolveAdminFromEnv(env);
135
+ if (!admin)
136
+ return undefined;
137
+ try {
138
+ return await probeTenantAdmin({ endpoint, username: admin.username, password: admin.password });
139
+ }
140
+ catch {
141
+ // 探测本身失败不该掩盖原始报错:按"不具备管理面"处理即可。
142
+ return undefined;
143
+ }
144
+ }
145
+ function firstNonEmpty(values) {
146
+ for (const value of values) {
147
+ if (typeof value === 'string' && value.trim())
148
+ return value.trim();
149
+ }
150
+ return undefined;
151
+ }
152
+ //# sourceMappingURL=guide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guide.js","sourceRoot":"","sources":["../../src/tenant/guide.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * 平台管理面能力探测(tenant probe)。
3
+ *
4
+ * 为什么要它:`profile import` / `login` 撞墙时,CLI 必须先知道"这个人到底是不是
5
+ * 管理员"才能给出正确的下一步。判据不能用"本机有没有 DASHBOARD_* 凭据"——持有凭据
6
+ * 与被授予权限是两件事(运维把服务端全量 env 发给客户排查问题,客户就"被管理员"了)。
7
+ * 所以这里用一次**只读**调用去问平台,按回答分流。
8
+ *
9
+ * 关键陷阱(方案文档 G3):Studio 的列表接口在「元数据查询失败」时同样返回
10
+ * `200 + { tenants: [], multiTenantMode: true }`(见 pages/api/platform/tenants/index.ts
11
+ * 的三处 200 空返回)。只看 HTTP 状态会把"平台库有问题/存量 schema 漂移"误报成
12
+ * "你没有权限 / 这个实例没有租户"。本模块用详情接口做二次判别:
13
+ * GET /api/platform/tenants/{不存在的 id}
14
+ * → 404 = 查询链路健康 ⇒ 空列表确实是"没有租户"
15
+ * → 500 = 查询本身失败 ⇒ 元数据不可读(多半是 schema 漂移)
16
+ * 这条判据来自 [id].ts 的实现(result.error → 500,未找到 → 404),非猜测。
17
+ */
18
+ import { type RedactedTenant } from '@polardbx/pxbaas-client';
19
+ export type ProbeStatus =
20
+ /** 具备平台管理面能力 */
21
+ 'admin'
22
+ /** 凭据无效/无权限(401/403) */
23
+ | 'not-admin'
24
+ /** 该实例不是多租户实例(multiTenantMode=false) */
25
+ | 'single-tenant'
26
+ /** 元数据不可读:列表为空且详情探测失败(G3/G5) */
27
+ | 'metadata-unreadable'
28
+ /** 网络不可达 / 端点错误 */
29
+ | 'unreachable';
30
+ export interface TenantProbeResult {
31
+ status: ProbeStatus;
32
+ /** 是否可执行租户管理写操作。仅 admin 为 true。 */
33
+ adminCapable: boolean;
34
+ endpoint: string;
35
+ multiTenantMode: boolean;
36
+ /** 仅 admin / metadata-unreadable 时可能有值(后者通常为空)。 */
37
+ tenants?: RedactedTenant[];
38
+ /** 一句话结论,可直接进诊断输出。 */
39
+ detail: string;
40
+ /** 归一化错误码(TENANT_ADMIN_UNAUTHORIZED 等),便于 --json 消费方分支。 */
41
+ errorCode?: string;
42
+ }
43
+ export interface AdminContext {
44
+ endpoint: string;
45
+ username: string;
46
+ password: string;
47
+ profileName: string;
48
+ }
49
+ /**
50
+ * 从当前(或指定)profile 的钥匙串账目里取管理凭据。
51
+ * 缺失时抛可直接展示的错误 —— 调用方不需要再判断"是 CLI 坏了还是没登录"。
52
+ */
53
+ export declare function loadAdminContext(options?: {
54
+ profile?: string;
55
+ }): Promise<AdminContext>;
56
+ export interface ProbeInput {
57
+ endpoint: string;
58
+ username: string;
59
+ password: string;
60
+ /** 探测用租户列表的条数上限无关;此值仅用于详情判别的超时。 */
61
+ timeout?: number;
62
+ }
63
+ /** 用给定凭据探测管理面能力。纯只读:不会发出任何写请求。 */
64
+ export declare function probeTenantAdmin(input: ProbeInput): Promise<TenantProbeResult>;
65
+ //# sourceMappingURL=probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/tenant/probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAMjC,MAAM,MAAM,WAAW;AACrB,gBAAgB;AACd,OAAO;AACT,wBAAwB;GACtB,WAAW;AACb,wCAAwC;GACtC,eAAe;AACjB,gCAAgC;GAC9B,qBAAqB;AACvB,mBAAmB;GACjB,aAAa,CAAC;AAElB,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,mCAAmC;IACnC,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,mDAAmD;IACnD,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CA8BhG;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,kCAAkC;AAClC,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkEpF"}