@cloudbase/toolbox 0.7.15 → 0.7.16-beta.0

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.
@@ -27,6 +27,8 @@ export interface WebAuthOptions {
27
27
  */
28
28
  throwError?: boolean;
29
29
  getAuthUrl?: (rawUrl: string) => string;
30
+ noBrowser?: boolean;
31
+ callbackTimeout?: number;
30
32
  }
31
33
  export interface LoginByApiSecretOptions {
32
34
  /**
package/lib/auth/index.js CHANGED
@@ -80,7 +80,7 @@ class AuthSupervisor {
80
80
  */
81
81
  loginByWebAuth(options = {}) {
82
82
  return __awaiter(this, void 0, void 0, function* () {
83
- const { getAuthUrl, throwError } = options;
83
+ const { getAuthUrl, throwError, noBrowser, callbackTimeout } = options;
84
84
  if (this.cacheCredential && this.needCache && !this.isCacheExpire()) {
85
85
  return this.cacheCredential;
86
86
  }
@@ -90,7 +90,9 @@ class AuthSupervisor {
90
90
  return credential;
91
91
  // 兼容临时秘钥
92
92
  credential = yield (0, web_auth_1.getAuthTokenFromWeb)({
93
- getAuthUrl
93
+ getAuthUrl,
94
+ noBrowser,
95
+ callbackTimeout
94
96
  });
95
97
  try {
96
98
  yield (0, common_1.checkAuth)(credential, this.requestConfig);
@@ -1,4 +1,6 @@
1
1
  import { Credential } from '../types';
2
2
  export declare function getAuthTokenFromWeb(options?: {
3
3
  getAuthUrl?: (rawUrl: string) => string;
4
+ noBrowser?: boolean;
5
+ callbackTimeout?: number;
4
6
  }): Promise<Credential>;
@@ -14,27 +14,38 @@ const common_1 = require("./common");
14
14
  const coding_1 = require("../coding");
15
15
  const web_1 = require("../web");
16
16
  const system_1 = require("../system");
17
- const CliAuthBaseUrl = 'https://tcb.cloud.tencent.com/dev#/cli-auth';
17
+ const CliAuthBaseUrl = 'https://tcb.cloud.tencent.com/dev';
18
18
  // 打开云开发控制台,获取授权
19
19
  function getAuthTokenFromWeb(options = {}) {
20
20
  return __awaiter(this, void 0, void 0, function* () {
21
- const { getAuthUrl } = options;
21
+ const { getAuthUrl, noBrowser, callbackTimeout } = options;
22
22
  const mac = yield (0, system_1.getMacAddress)();
23
23
  const os = (0, system_1.getOSInfo)();
24
24
  const macHash = (0, coding_1.md5Encoding)(mac);
25
25
  const query = yield (0, web_1.getDataFromWeb)((port) => {
26
+ const callbackUrl = `http://127.0.0.1:${port}`;
27
+ const encodedQuery = `port=${encodeURIComponent(String(port))}`
28
+ + `&hash=${encodeURIComponent(macHash)}`
29
+ + `&mac=${encodeURIComponent(mac)}`
30
+ + `&os=${encodeURIComponent(os)}`
31
+ + '&from=cli';
32
+ const encodedCallbackUrl = encodeURIComponent(callbackUrl);
26
33
  // 授权链接
27
- let cliAuthUrl = `${CliAuthBaseUrl}?port=${port}&hash=${macHash}&mac=${mac}&os=${os}&from=cli`;
34
+ const rawAuthUrl = `${CliAuthBaseUrl}?authCallbackUrl=${encodedCallbackUrl}#/cli-auth?${encodedQuery}`;
35
+ let cliAuthUrl = rawAuthUrl;
28
36
  if (getAuthUrl) {
29
37
  try {
30
- cliAuthUrl = getAuthUrl(`${CliAuthBaseUrl}?port=${port}&hash=${macHash}&mac=${mac}&os=${os}&from=cli`);
38
+ cliAuthUrl = getAuthUrl(rawAuthUrl);
31
39
  }
32
40
  catch (error) {
33
41
  // 忽略错误
34
42
  }
35
43
  }
36
44
  return cliAuthUrl;
37
- }, 'login');
45
+ }, 'login', {
46
+ noBrowser,
47
+ callbackTimeout
48
+ });
38
49
  const credential = (0, common_1.resolveCredential)(query);
39
50
  return credential;
40
51
  });
@@ -15,12 +15,32 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.getPort = void 0;
16
16
  const portfinder_1 = __importDefault(require("portfinder"));
17
17
  // 默认端口
18
- const DEFAULT_PORT = 9012;
18
+ const LOCAL_DEFAULT_PORT = 9012;
19
+ const REMOTE_DEFAULT_PORT = 19012;
20
+ function isRemoteEnvironment() {
21
+ return Boolean(process.env.SSH_CONNECTION
22
+ || process.env.SSH_CLIENT
23
+ || process.env.SSH_TTY
24
+ || process.env.VSCODE_IPC_HOOK_CLI
25
+ || process.env.VSCODE_AGENT_FOLDER
26
+ || process.env.REMOTE_CONTAINERS
27
+ || process.env.CODESPACES
28
+ || process.env.GITPOD_WORKSPACE_ID);
29
+ }
30
+ function getStartPort() {
31
+ const customStartPort = Number(process.env.TCB_WEB_LOGIN_START_PORT);
32
+ if (Number.isInteger(customStartPort) && customStartPort > 0 && customStartPort < 65535) {
33
+ return customStartPort;
34
+ }
35
+ return isRemoteEnvironment() ? REMOTE_DEFAULT_PORT : LOCAL_DEFAULT_PORT;
36
+ }
19
37
  // 获取本地可用端口
20
38
  function getPort() {
21
39
  return __awaiter(this, void 0, void 0, function* () {
40
+ const startPort = getStartPort();
22
41
  const port = yield portfinder_1.default.getPortPromise({
23
- port: DEFAULT_PORT
42
+ port: startPort,
43
+ stopPort: 65535
24
44
  });
25
45
  return port;
26
46
  });
package/lib/web/web.d.ts CHANGED
@@ -3,4 +3,8 @@ export interface IQuery {
3
3
  }
4
4
  export type GetUrlFn = (port: number) => string;
5
5
  export type CheckFn = (query: IQuery) => Promise<void>;
6
- export declare function getDataFromWeb<T extends IQuery>(getUrl: GetUrlFn, type: 'login' | 'getData'): Promise<T>;
6
+ export interface GetDataFromWebOptions {
7
+ noBrowser?: boolean;
8
+ callbackTimeout?: number;
9
+ }
10
+ export declare function getDataFromWeb<T extends IQuery>(getUrl: GetUrlFn, type: 'login' | 'getData', options?: GetDataFromWebOptions): Promise<T>;
package/lib/web/web.js CHANGED
@@ -19,6 +19,7 @@ const http_1 = __importDefault(require("http"));
19
19
  const system_1 = require("../system");
20
20
  const error_1 = require("../error");
21
21
  const html_1 = require("../html");
22
+ const child_process_1 = require("child_process");
22
23
  // 创建本地 Web 服务器,接受 Web 控制台传入的信息
23
24
  function createLocalServer() {
24
25
  return __awaiter(this, void 0, void 0, function* () {
@@ -52,23 +53,81 @@ function respondWithFile(options) {
52
53
  resolve();
53
54
  });
54
55
  }
56
+ function isTruthyFlag(value) {
57
+ if (!value) {
58
+ return false;
59
+ }
60
+ return ['1', 'true', 'yes', 'on'].includes(String(value).trim().toLowerCase());
61
+ }
62
+ function isVSCodeEnvironment() {
63
+ return !!(process.env.VSCODE_PID || process.env.VSCODE_CWD || process.env.TERM_PROGRAM === 'vscode');
64
+ }
65
+ function openUrlByVSCode(url) {
66
+ return __awaiter(this, void 0, void 0, function* () {
67
+ return new Promise((resolve, reject) => {
68
+ (0, child_process_1.execFile)('code', ['--open-url', url], (error) => {
69
+ if (error) {
70
+ reject(error);
71
+ return;
72
+ }
73
+ resolve();
74
+ });
75
+ });
76
+ });
77
+ }
55
78
  // 从 Web 页面中获取数据
56
- function getDataFromWeb(getUrl, type) {
79
+ function getDataFromWeb(getUrl, type, options = {}) {
80
+ var _a, _b;
57
81
  return __awaiter(this, void 0, void 0, function* () {
58
82
  const { server, port } = yield createLocalServer();
59
- const url = getUrl(port);
60
- // url 转码, 避免 wsl 无法正常打开地址
61
- // https://www.npmjs.com/package/open#url
62
- // https://github.com/sindresorhus/open/blob/master/index.js#L48
63
- try {
64
- yield (0, open_1.default)(url, { url: true });
83
+ const noBrowser = (_a = options.noBrowser) !== null && _a !== void 0 ? _a : isTruthyFlag(process.env.TCB_NO_BROWSER);
84
+ const callbackTimeout = (_b = options.callbackTimeout) !== null && _b !== void 0 ? _b : 180000;
85
+ if (!Number.isFinite(callbackTimeout) || callbackTimeout <= 0) {
86
+ throw new error_1.CloudBaseError('callbackTimeout must be a positive number');
65
87
  }
66
- catch (e) {
67
- throw new error_1.CloudBaseError('打开浏览器失败,请尝试使用其他登录方式');
88
+ const url = getUrl(port);
89
+ console.log('若链接未自动打开,请手动复制此链接至浏览器,或尝试使用其他登录方式:');
90
+ console.log(`\n${url}`);
91
+ if (!noBrowser) {
92
+ // 对 url 转码, 避免 wsl 无法正常打开地址
93
+ // https://www.npmjs.com/package/open#url
94
+ // https://github.com/sindresorhus/open/blob/master/index.js#L48
95
+ try {
96
+ yield (0, open_1.default)(url, { url: true });
97
+ }
98
+ catch (e) {
99
+ const code = (e === null || e === void 0 ? void 0 : e.code) || 'UNKNOWN';
100
+ console.warn(`自动打开浏览器失败(${code})。`);
101
+ if (isVSCodeEnvironment()) {
102
+ try {
103
+ yield openUrlByVSCode(url);
104
+ }
105
+ catch (vscodeError) {
106
+ // ignore error
107
+ }
108
+ }
109
+ }
68
110
  }
69
111
  return new Promise((resolve, reject) => {
112
+ let finished = false;
113
+ const timer = setTimeout(() => {
114
+ if (finished) {
115
+ return;
116
+ }
117
+ finished = true;
118
+ server.close();
119
+ reject(new error_1.CloudBaseError(`等待浏览器授权回调超时(${Math.floor(callbackTimeout / 1000)}s)!`));
120
+ }, callbackTimeout);
121
+ const finish = (fn) => {
122
+ if (finished) {
123
+ return;
124
+ }
125
+ finished = true;
126
+ clearTimeout(timer);
127
+ fn();
128
+ };
70
129
  server.on('request', (req, res) => {
71
- const { url } = req;
130
+ const url = req.url || '/';
72
131
  const { query } = query_string_1.default.parseUrl(url);
73
132
  // 响应 HTML 文件
74
133
  if (query === null || query === void 0 ? void 0 : query.html) {
@@ -78,11 +137,15 @@ function getDataFromWeb(getUrl, type) {
78
137
  statusCode: 200,
79
138
  filename: `${type}Success`
80
139
  }).then(() => {
81
- server.close();
82
- resolve(query);
140
+ finish(() => {
141
+ server.close();
142
+ resolve(query);
143
+ });
83
144
  }).catch((e) => {
84
- server.close();
85
- reject(e);
145
+ finish(() => {
146
+ server.close();
147
+ reject(e);
148
+ });
86
149
  });
87
150
  }
88
151
  // CORS 响应普通文本
@@ -98,7 +161,9 @@ function getDataFromWeb(getUrl, type) {
98
161
  if (req.method !== 'OPTIONS') {
99
162
  server.close();
100
163
  }
101
- resolve(query);
164
+ finish(() => {
165
+ resolve(query);
166
+ });
102
167
  });
103
168
  });
104
169
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/toolbox",
3
- "version": "0.7.15",
3
+ "version": "0.7.16-beta.0",
4
4
  "description": "The toolbox for cloudbase",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -63,7 +63,7 @@
63
63
  },
64
64
  "husky": {
65
65
  "hooks": {
66
- "pre-commit": "npm run build && git add ."
66
+ "pre-commit": "npm run build"
67
67
  }
68
68
  },
69
69
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"