@optima-chat/ads-cli 0.5.0 → 0.6.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.
- package/dist/commands/account/check.d.ts.map +1 -1
- package/dist/commands/account/check.js +26 -123
- package/dist/commands/account/check.js.map +1 -1
- package/dist/commands/account/create.d.ts.map +1 -1
- package/dist/commands/account/create.js +0 -40
- package/dist/commands/account/create.js.map +1 -1
- package/dist/commands/ad/create.js +2 -2
- package/dist/commands/ad/create.js.map +1 -1
- package/dist/commands/ad/delete.js +2 -2
- package/dist/commands/ad/delete.js.map +1 -1
- package/dist/commands/ad/info.js +2 -2
- package/dist/commands/ad/info.js.map +1 -1
- package/dist/commands/ad/list.js +2 -2
- package/dist/commands/ad/list.js.map +1 -1
- package/dist/commands/ad/update.d.ts +1 -1
- package/dist/commands/ad/update.js +3 -3
- package/dist/commands/ad/update.js.map +1 -1
- package/dist/commands/ad-group/create.d.ts.map +1 -1
- package/dist/commands/ad-group/create.js +10 -16
- package/dist/commands/ad-group/create.js.map +1 -1
- package/dist/commands/ad-group/delete.js +2 -2
- package/dist/commands/ad-group/delete.js.map +1 -1
- package/dist/commands/ad-group/info.js +2 -2
- package/dist/commands/ad-group/info.js.map +1 -1
- package/dist/commands/ad-group/list.d.ts.map +1 -1
- package/dist/commands/ad-group/list.js +4 -3
- package/dist/commands/ad-group/list.js.map +1 -1
- package/dist/commands/ad-group/update.js +2 -2
- package/dist/commands/ad-group/update.js.map +1 -1
- package/dist/commands/auth/login.d.ts +2 -4
- package/dist/commands/auth/login.d.ts.map +1 -1
- package/dist/commands/auth/login.js +68 -135
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/auth/logout.d.ts +2 -0
- package/dist/commands/auth/logout.d.ts.map +1 -1
- package/dist/commands/auth/logout.js +19 -17
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/auth/status.d.ts +2 -0
- package/dist/commands/auth/status.d.ts.map +1 -1
- package/dist/commands/auth/status.js +36 -21
- package/dist/commands/auth/status.js.map +1 -1
- package/dist/commands/campaign/create.d.ts.map +1 -1
- package/dist/commands/campaign/create.js +9 -15
- package/dist/commands/campaign/create.js.map +1 -1
- package/dist/commands/campaign/delete.js +2 -2
- package/dist/commands/campaign/delete.js.map +1 -1
- package/dist/commands/campaign/info.js +2 -2
- package/dist/commands/campaign/info.js.map +1 -1
- package/dist/commands/campaign/list.js +2 -2
- package/dist/commands/campaign/list.js.map +1 -1
- package/dist/commands/campaign/update.js +2 -2
- package/dist/commands/campaign/update.js.map +1 -1
- package/dist/commands/keyword/add.d.ts.map +1 -1
- package/dist/commands/keyword/add.js +7 -6
- package/dist/commands/keyword/add.js.map +1 -1
- package/dist/commands/keyword/delete.js +2 -2
- package/dist/commands/keyword/delete.js.map +1 -1
- package/dist/commands/keyword/list.d.ts.map +1 -1
- package/dist/commands/keyword/list.js +4 -3
- package/dist/commands/keyword/list.js.map +1 -1
- package/dist/commands/keyword/update.js +2 -2
- package/dist/commands/keyword/update.js.map +1 -1
- package/dist/commands/query.js +2 -2
- package/dist/commands/query.js.map +1 -1
- package/dist/lib/api-client.d.ts +2 -1
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +7 -9
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/auth-types.d.ts +74 -0
- package/dist/lib/auth-types.d.ts.map +1 -0
- package/dist/lib/auth-types.js +24 -0
- package/dist/lib/auth-types.js.map +1 -0
- package/dist/lib/device-flow.d.ts +11 -0
- package/dist/lib/device-flow.d.ts.map +1 -0
- package/dist/lib/device-flow.js +111 -0
- package/dist/lib/device-flow.js.map +1 -0
- package/dist/lib/token-store.d.ts +43 -32
- package/dist/lib/token-store.d.ts.map +1 -1
- package/dist/lib/token-store.js +183 -81
- package/dist/lib/token-store.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Types - 认证相关类型定义
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* 环境配置
|
|
6
|
+
*/
|
|
7
|
+
export const ENV_CONFIG = {
|
|
8
|
+
ci: {
|
|
9
|
+
authUrl: 'https://auth.optima.chat',
|
|
10
|
+
clientId: 'google-ads-cli-dev',
|
|
11
|
+
adsApiUrl: 'https://ads-api.optima.onl', // CI 环境用 prod API
|
|
12
|
+
},
|
|
13
|
+
stage: {
|
|
14
|
+
authUrl: 'https://auth.stage.optima.onl',
|
|
15
|
+
clientId: 'google-ads-cli-stage',
|
|
16
|
+
adsApiUrl: 'https://ads-api.stage.optima.onl',
|
|
17
|
+
},
|
|
18
|
+
prod: {
|
|
19
|
+
authUrl: 'https://auth.optima.onl',
|
|
20
|
+
clientId: 'google-ads-cli-prod',
|
|
21
|
+
adsApiUrl: 'https://ads-api.optima.onl',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=auth-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-types.js","sourceRoot":"","sources":["../../src/lib/auth-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAGnB;IACF,EAAE,EAAE;QACF,OAAO,EAAE,0BAA0B;QACnC,QAAQ,EAAE,oBAAoB;QAC9B,SAAS,EAAE,4BAA4B,EAAE,kBAAkB;KAC5D;IACD,KAAK,EAAE;QACL,OAAO,EAAE,+BAA+B;QACxC,QAAQ,EAAE,sBAAsB;QAChC,SAAS,EAAE,kCAAkC;KAC9C;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,yBAAyB;QAClC,QAAQ,EAAE,qBAAqB;QAC/B,SAAS,EAAE,4BAA4B;KACxC;CACF,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Flow - OAuth2 设备授权流程
|
|
3
|
+
*
|
|
4
|
+
* 参考 optima-agent 实现,使用 Device Flow 进行登录
|
|
5
|
+
*/
|
|
6
|
+
import type { DeviceFlowCallbacks, Environment } from './auth-types.js';
|
|
7
|
+
/**
|
|
8
|
+
* 启动 Device Flow 登录
|
|
9
|
+
*/
|
|
10
|
+
export declare function startDeviceFlow(env: Environment, callbacks: DeviceFlowCallbacks, signal?: AbortSignal): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=device-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../../src/lib/device-flow.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAGV,mBAAmB,EACnB,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAazB;;GAEG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,mBAAmB,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAsHf"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Flow - OAuth2 设备授权流程
|
|
3
|
+
*
|
|
4
|
+
* 参考 optima-agent 实现,使用 Device Flow 进行登录
|
|
5
|
+
*/
|
|
6
|
+
import { ENV_CONFIG } from './auth-types.js';
|
|
7
|
+
import { saveToken, fetchUserInfo } from './token-store.js';
|
|
8
|
+
function sleep(ms) {
|
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 启动 Device Flow 登录
|
|
13
|
+
*/
|
|
14
|
+
export async function startDeviceFlow(env, callbacks, signal) {
|
|
15
|
+
const { authUrl, clientId } = ENV_CONFIG[env];
|
|
16
|
+
// 1. 请求 device code
|
|
17
|
+
const codeRes = await fetch(`${authUrl}/api/v1/oauth/device/authorize`, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: { 'Content-Type': 'application/json' },
|
|
20
|
+
body: JSON.stringify({ client_id: clientId }),
|
|
21
|
+
signal,
|
|
22
|
+
});
|
|
23
|
+
if (!codeRes.ok) {
|
|
24
|
+
const errorText = await codeRes.text();
|
|
25
|
+
throw new Error(`Failed to get device code: ${codeRes.status} ${errorText}`);
|
|
26
|
+
}
|
|
27
|
+
const codeData = (await codeRes.json());
|
|
28
|
+
// 2. 通知 UI 显示 code(优先使用带 code 的完整 URL)
|
|
29
|
+
const verificationUrl = codeData.verification_uri_complete || codeData.verification_uri;
|
|
30
|
+
callbacks.onCodeReceived(codeData.user_code, verificationUrl);
|
|
31
|
+
// 3. 轮询获取 token
|
|
32
|
+
const pollInterval = (codeData.interval || 5) * 1000;
|
|
33
|
+
const expiresAt = Date.now() + codeData.expires_in * 1000;
|
|
34
|
+
while (Date.now() < expiresAt) {
|
|
35
|
+
if (signal?.aborted) {
|
|
36
|
+
throw new Error('Login cancelled');
|
|
37
|
+
}
|
|
38
|
+
callbacks.onPolling?.();
|
|
39
|
+
await sleep(pollInterval);
|
|
40
|
+
if (signal?.aborted) {
|
|
41
|
+
throw new Error('Login cancelled');
|
|
42
|
+
}
|
|
43
|
+
const tokenRes = await fetch(`${authUrl}/api/v1/oauth/device/token`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
46
|
+
body: new URLSearchParams({
|
|
47
|
+
device_code: codeData.device_code,
|
|
48
|
+
client_id: clientId,
|
|
49
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
50
|
+
}),
|
|
51
|
+
signal,
|
|
52
|
+
});
|
|
53
|
+
// 解析响应
|
|
54
|
+
let responseBody;
|
|
55
|
+
try {
|
|
56
|
+
responseBody = await tokenRes.text();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
responseBody = '';
|
|
60
|
+
}
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
62
|
+
let data;
|
|
63
|
+
try {
|
|
64
|
+
data = JSON.parse(responseBody);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
callbacks.onError(`Invalid response: ${responseBody}`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// 成功获取 token(有 access_token 且无 error)
|
|
71
|
+
if (data.access_token && !data.error) {
|
|
72
|
+
const tokenData = data;
|
|
73
|
+
// 获取用户信息
|
|
74
|
+
let userInfo;
|
|
75
|
+
try {
|
|
76
|
+
userInfo = await fetchUserInfo(tokenData.access_token, env);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// 用户信息获取失败不影响登录
|
|
80
|
+
}
|
|
81
|
+
// 保存 token
|
|
82
|
+
saveToken(tokenData, env, userInfo);
|
|
83
|
+
callbacks.onSuccess(userInfo || { id: '', email: '' });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// 处理错误响应
|
|
87
|
+
const error = data;
|
|
88
|
+
// authorization_pending 是正常状态,继续轮询
|
|
89
|
+
if (error.error === 'authorization_pending') {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (error.error === 'slow_down') {
|
|
93
|
+
await sleep(5000); // 额外等待
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (error.error === 'expired_token') {
|
|
97
|
+
callbacks.onError('登录超时,请重试');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (error.error === 'access_denied') {
|
|
101
|
+
callbacks.onError('登录被拒绝');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// 其他错误,显示详情
|
|
105
|
+
const errorMsg = error.error_description || error.error || `HTTP ${tokenRes.status}`;
|
|
106
|
+
callbacks.onError(errorMsg);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
callbacks.onError('登录超时');
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=device-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-flow.js","sourceRoot":"","sources":["../../src/lib/device-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,43 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Token Store - Optima JWT Token 存储和管理
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 2. ~/.optima/token.json 文件
|
|
4
|
+
* Token 存储位置:~/.optima/token.json
|
|
5
|
+
* 与 optima-agent 兼容
|
|
7
6
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
7
|
+
import type { TokenData, TokenResponse, UserInfo, AuthStatus, Environment } from './auth-types.js';
|
|
8
|
+
/**
|
|
9
|
+
* 确保 ~/.optima 目录存在
|
|
10
|
+
*/
|
|
11
|
+
export declare function ensureOptimaDir(): void;
|
|
12
|
+
/**
|
|
13
|
+
* 保存 Token
|
|
14
|
+
*/
|
|
15
|
+
export declare function saveToken(response: TokenResponse, env: Environment, user?: UserInfo): void;
|
|
16
|
+
/**
|
|
17
|
+
* 读取 Token(按优先级)
|
|
18
|
+
*/
|
|
19
|
+
export declare function getToken(): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* 读取完整 Token 数据
|
|
22
|
+
*/
|
|
23
|
+
export declare function getTokenData(): TokenData | null;
|
|
24
|
+
/**
|
|
25
|
+
* 清除 Token
|
|
26
|
+
*/
|
|
27
|
+
export declare function clearToken(): void;
|
|
28
|
+
/**
|
|
29
|
+
* 检查 Token 是否过期
|
|
30
|
+
*/
|
|
31
|
+
export declare function isTokenExpired(data?: TokenData | null): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* 获取用户信息
|
|
34
|
+
*/
|
|
35
|
+
export declare function fetchUserInfo(accessToken: string, env: Environment): Promise<UserInfo>;
|
|
29
36
|
/**
|
|
30
|
-
*
|
|
31
|
-
* @deprecated 使用 TokenStore 类替代
|
|
37
|
+
* 获取认证状态
|
|
32
38
|
*/
|
|
33
|
-
export declare function
|
|
39
|
+
export declare function getAuthStatus(): Promise<AuthStatus>;
|
|
34
40
|
/**
|
|
35
|
-
*
|
|
36
|
-
* @deprecated 使用 TokenStore 类替代
|
|
41
|
+
* 刷新 Token
|
|
37
42
|
*/
|
|
38
|
-
export declare function
|
|
43
|
+
export declare function refreshToken(): Promise<boolean>;
|
|
39
44
|
/**
|
|
40
|
-
*
|
|
45
|
+
* 获取当前环境的 Ads API URL
|
|
41
46
|
*/
|
|
42
|
-
export declare function
|
|
47
|
+
export declare function getAdsApiUrl(): string;
|
|
48
|
+
export declare class TokenStore {
|
|
49
|
+
getToken(): Promise<string | null>;
|
|
50
|
+
hasToken(): Promise<boolean>;
|
|
51
|
+
saveToken(accessToken: string, refreshToken?: string): Promise<void>;
|
|
52
|
+
clearToken(): Promise<void>;
|
|
53
|
+
}
|
|
43
54
|
//# sourceMappingURL=token-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/lib/token-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/lib/token-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,QAAQ,EACR,UAAU,EACV,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAMzB;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAItC;AAED;;GAEG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,WAAW,EAChB,IAAI,CAAC,EAAE,QAAQ,GACd,IAAI,CAaN;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAaxC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAqB/C;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAIjC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAM/D;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,QAAQ,CAAC,CAYnB;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAuCzD;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CA6BrD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAIrC;AAID,qBAAa,UAAU;IACf,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIlC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAI5B,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcpE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAGlC"}
|
package/dist/lib/token-store.js
CHANGED
|
@@ -1,103 +1,205 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Token Store - Optima JWT Token 存储和管理
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 2. ~/.optima/token.json 文件
|
|
4
|
+
* Token 存储位置:~/.optima/token.json
|
|
5
|
+
* 与 optima-agent 兼容
|
|
7
6
|
*/
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { homedir } from 'os';
|
|
10
|
+
import { ENV_CONFIG } from './auth-types.js';
|
|
11
|
+
const OPTIMA_DIR = join(homedir(), '.optima');
|
|
12
|
+
const TOKEN_FILE = join(OPTIMA_DIR, 'token.json');
|
|
13
|
+
/**
|
|
14
|
+
* 确保 ~/.optima 目录存在
|
|
15
|
+
*/
|
|
16
|
+
export function ensureOptimaDir() {
|
|
17
|
+
if (!existsSync(OPTIMA_DIR)) {
|
|
18
|
+
mkdirSync(OPTIMA_DIR, { recursive: true, mode: 0o700 });
|
|
15
19
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 保存 Token
|
|
23
|
+
*/
|
|
24
|
+
export function saveToken(response, env, user) {
|
|
25
|
+
ensureOptimaDir();
|
|
26
|
+
const tokenData = {
|
|
27
|
+
env,
|
|
28
|
+
access_token: response.access_token,
|
|
29
|
+
refresh_token: response.refresh_token,
|
|
30
|
+
token_type: response.token_type,
|
|
31
|
+
expires_at: Date.now() + response.expires_in * 1000,
|
|
32
|
+
user,
|
|
33
|
+
};
|
|
34
|
+
writeFileSync(TOKEN_FILE, JSON.stringify(tokenData, null, 2), { mode: 0o600 });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 读取 Token(按优先级)
|
|
38
|
+
*/
|
|
39
|
+
export function getToken() {
|
|
40
|
+
// 1. 环境变量
|
|
41
|
+
if (process.env.OPTIMA_TOKEN) {
|
|
42
|
+
return process.env.OPTIMA_TOKEN;
|
|
40
43
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const token = await this.getToken();
|
|
46
|
-
return !!token;
|
|
44
|
+
// 2. token.json 文件
|
|
45
|
+
const data = getTokenData();
|
|
46
|
+
if (data) {
|
|
47
|
+
return data.access_token;
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 读取完整 Token 数据
|
|
53
|
+
*/
|
|
54
|
+
export function getTokenData() {
|
|
55
|
+
if (!existsSync(TOKEN_FILE)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const content = readFileSync(TOKEN_FILE, 'utf-8');
|
|
60
|
+
const data = JSON.parse(content);
|
|
61
|
+
// 兼容两种格式
|
|
62
|
+
return {
|
|
63
|
+
env: data.env || 'prod',
|
|
64
|
+
access_token: data.access_token || data.accessToken,
|
|
65
|
+
refresh_token: data.refresh_token || data.refreshToken,
|
|
66
|
+
token_type: data.token_type || 'Bearer',
|
|
67
|
+
expires_at: data.expires_at || data.expiresAt || 0,
|
|
68
|
+
user: data.user,
|
|
60
69
|
};
|
|
61
|
-
fs.writeFileSync(this.tokenFilePath, JSON.stringify(token, null, 2));
|
|
62
70
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*/
|
|
66
|
-
async clearToken() {
|
|
67
|
-
if (fs.existsSync(this.tokenFilePath)) {
|
|
68
|
-
fs.unlinkSync(this.tokenFilePath);
|
|
69
|
-
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
/**
|
|
73
|
-
*
|
|
74
|
-
* @deprecated 使用 TokenStore 类替代
|
|
76
|
+
* 清除 Token
|
|
75
77
|
*/
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return null;
|
|
78
|
+
export function clearToken() {
|
|
79
|
+
if (existsSync(TOKEN_FILE)) {
|
|
80
|
+
unlinkSync(TOKEN_FILE);
|
|
80
81
|
}
|
|
81
|
-
return {
|
|
82
|
-
access_token: '',
|
|
83
|
-
refresh_token: refreshToken,
|
|
84
|
-
expires_at: 0,
|
|
85
|
-
scope: 'https://www.googleapis.com/auth/adwords',
|
|
86
|
-
token_type: 'Bearer',
|
|
87
|
-
};
|
|
88
82
|
}
|
|
89
83
|
/**
|
|
90
|
-
*
|
|
91
|
-
* @deprecated 使用 TokenStore 类替代
|
|
84
|
+
* 检查 Token 是否过期
|
|
92
85
|
*/
|
|
93
|
-
export function
|
|
94
|
-
|
|
86
|
+
export function isTokenExpired(data) {
|
|
87
|
+
const tokenData = data ?? getTokenData();
|
|
88
|
+
if (!tokenData)
|
|
89
|
+
return true;
|
|
90
|
+
// 提前 5 分钟视为过期
|
|
91
|
+
return Date.now() > tokenData.expires_at - 5 * 60 * 1000;
|
|
95
92
|
}
|
|
96
93
|
/**
|
|
97
|
-
*
|
|
94
|
+
* 获取用户信息
|
|
98
95
|
*/
|
|
99
|
-
export function
|
|
100
|
-
const
|
|
101
|
-
|
|
96
|
+
export async function fetchUserInfo(accessToken, env) {
|
|
97
|
+
const { authUrl } = ENV_CONFIG[env];
|
|
98
|
+
const res = await fetch(`${authUrl}/api/v1/users/me`, {
|
|
99
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
100
|
+
});
|
|
101
|
+
if (!res.ok) {
|
|
102
|
+
throw new Error('Failed to fetch user info');
|
|
103
|
+
}
|
|
104
|
+
return (await res.json());
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 获取认证状态
|
|
108
|
+
*/
|
|
109
|
+
export async function getAuthStatus() {
|
|
110
|
+
const data = getTokenData();
|
|
111
|
+
if (!data) {
|
|
112
|
+
return { loggedIn: false };
|
|
113
|
+
}
|
|
114
|
+
if (isTokenExpired(data)) {
|
|
115
|
+
return { loggedIn: false };
|
|
116
|
+
}
|
|
117
|
+
// 如果有缓存的用户信息,直接使用
|
|
118
|
+
if (data.user) {
|
|
119
|
+
return {
|
|
120
|
+
loggedIn: true,
|
|
121
|
+
env: data.env,
|
|
122
|
+
user: data.user,
|
|
123
|
+
expiresAt: data.expires_at,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// 否则从服务端获取
|
|
127
|
+
try {
|
|
128
|
+
const user = await fetchUserInfo(data.access_token, data.env);
|
|
129
|
+
// 更新缓存
|
|
130
|
+
const updatedData = { ...data, user };
|
|
131
|
+
writeFileSync(TOKEN_FILE, JSON.stringify(updatedData, null, 2), {
|
|
132
|
+
mode: 0o600,
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
loggedIn: true,
|
|
136
|
+
env: data.env,
|
|
137
|
+
user,
|
|
138
|
+
expiresAt: data.expires_at,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return { loggedIn: false };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 刷新 Token
|
|
147
|
+
*/
|
|
148
|
+
export async function refreshToken() {
|
|
149
|
+
const data = getTokenData();
|
|
150
|
+
if (!data?.refresh_token) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const { authUrl, clientId } = ENV_CONFIG[data.env];
|
|
154
|
+
try {
|
|
155
|
+
const res = await fetch(`${authUrl}/api/v1/oauth/token`, {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
158
|
+
body: new URLSearchParams({
|
|
159
|
+
grant_type: 'refresh_token',
|
|
160
|
+
refresh_token: data.refresh_token,
|
|
161
|
+
client_id: clientId,
|
|
162
|
+
}),
|
|
163
|
+
});
|
|
164
|
+
if (!res.ok) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
const tokenResponse = (await res.json());
|
|
168
|
+
saveToken(tokenResponse, data.env, data.user);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 获取当前环境的 Ads API URL
|
|
177
|
+
*/
|
|
178
|
+
export function getAdsApiUrl() {
|
|
179
|
+
const data = getTokenData();
|
|
180
|
+
const env = data?.env || 'prod';
|
|
181
|
+
return process.env.ADS_BACKEND_URL || ENV_CONFIG[env].adsApiUrl;
|
|
182
|
+
}
|
|
183
|
+
// ============ TokenStore class for backward compatibility ============
|
|
184
|
+
export class TokenStore {
|
|
185
|
+
async getToken() {
|
|
186
|
+
return getToken();
|
|
187
|
+
}
|
|
188
|
+
async hasToken() {
|
|
189
|
+
return !!getToken();
|
|
190
|
+
}
|
|
191
|
+
async saveToken(accessToken, refreshToken) {
|
|
192
|
+
const data = getTokenData();
|
|
193
|
+
const env = data?.env || 'prod';
|
|
194
|
+
saveToken({
|
|
195
|
+
access_token: accessToken,
|
|
196
|
+
refresh_token: refreshToken,
|
|
197
|
+
token_type: 'Bearer',
|
|
198
|
+
expires_in: 24 * 60 * 60, // 24 hours
|
|
199
|
+
}, env);
|
|
200
|
+
}
|
|
201
|
+
async clearToken() {
|
|
202
|
+
clearToken();
|
|
203
|
+
}
|
|
102
204
|
}
|
|
103
205
|
//# sourceMappingURL=token-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/lib/token-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/lib/token-store.ts"],"names":[],"mappings":"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"}
|