@quantabit/oauth-sdk 1.0.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/LICENSE +21 -0
- package/README.md +143 -0
- package/dist/index.cjs +1152 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +1124 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +97 -0
- package/types/index.d.ts +60 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1152 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sdkConfig = require('@quantabit/sdk-config');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* OAuth SDK - API 客户端
|
|
8
|
+
* OAuth 认证系统后端接口封装
|
|
9
|
+
*
|
|
10
|
+
* 使用 BaseApiClient 基类简化代码
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* OAuth API 客户端
|
|
16
|
+
*/
|
|
17
|
+
class OauthApiClient extends sdkConfig.BaseApiClient {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
super('/oauth', config);
|
|
20
|
+
}
|
|
21
|
+
_normalizeProviderArgs(providerOrOptions, options = {}) {
|
|
22
|
+
if (typeof providerOrOptions === 'object' && providerOrOptions !== null) {
|
|
23
|
+
const {
|
|
24
|
+
provider,
|
|
25
|
+
...params
|
|
26
|
+
} = providerOrOptions;
|
|
27
|
+
return {
|
|
28
|
+
provider,
|
|
29
|
+
params
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
provider: providerOrOptions,
|
|
34
|
+
params: options
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ============ OAuth 登录 ============
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 获取 OAuth 登录 URL
|
|
42
|
+
* @param {string} provider - 提供商(google, github, twitter, discord 等)
|
|
43
|
+
* @param {Object} options - 选项
|
|
44
|
+
*/
|
|
45
|
+
async getAuthUrl(provider, options = {}) {
|
|
46
|
+
const normalized = this._normalizeProviderArgs(provider, options);
|
|
47
|
+
return this.get(`/${normalized.provider}/auth-url`, normalized.params);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* OAuth 回调处理
|
|
52
|
+
* @param {string} provider - 提供商
|
|
53
|
+
* @param {string} code - 授权码
|
|
54
|
+
* @param {string} state - 状态
|
|
55
|
+
*/
|
|
56
|
+
async handleCallback(provider, code, state) {
|
|
57
|
+
if (typeof provider === 'object' && provider !== null) {
|
|
58
|
+
const {
|
|
59
|
+
provider: callbackProvider,
|
|
60
|
+
code: callbackCode,
|
|
61
|
+
state: callbackState
|
|
62
|
+
} = provider;
|
|
63
|
+
return this.post(`/${callbackProvider}/callback`, {
|
|
64
|
+
code: callbackCode,
|
|
65
|
+
state: callbackState
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return this.post(`/${provider}/callback`, {
|
|
69
|
+
code,
|
|
70
|
+
state
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 获取支持的 OAuth 提供商
|
|
76
|
+
*/
|
|
77
|
+
async getProviders() {
|
|
78
|
+
return this.get('/providers');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ============ 账号绑定 ============
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 绑定第三方账号
|
|
85
|
+
* @param {string} provider - 提供商
|
|
86
|
+
* @param {string} code - 授权码
|
|
87
|
+
*/
|
|
88
|
+
async bindAccount(provider, code) {
|
|
89
|
+
return this.post(`/${provider}/bind`, {
|
|
90
|
+
code
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 解绑第三方账号
|
|
96
|
+
* @param {string} provider - 提供商
|
|
97
|
+
*/
|
|
98
|
+
async unbindAccount(provider) {
|
|
99
|
+
return this.delete(`/${provider}/bind`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 解绑第三方账号(兼容 hooks 命名)
|
|
104
|
+
* @param {string} provider - 提供商
|
|
105
|
+
*/
|
|
106
|
+
async unlinkAccount(provider) {
|
|
107
|
+
return this.unbindAccount(provider);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 获取已绑定账号
|
|
112
|
+
*/
|
|
113
|
+
async getBoundAccounts() {
|
|
114
|
+
return this.get('/accounts');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 获取已关联账号(兼容旧版快捷方法命名)
|
|
119
|
+
*/
|
|
120
|
+
async getLinkedAccounts() {
|
|
121
|
+
return this.getBoundAccounts();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 检查账号绑定状态
|
|
126
|
+
* @param {string} provider - 提供商
|
|
127
|
+
*/
|
|
128
|
+
async checkBindStatus(provider) {
|
|
129
|
+
return this.get(`/${provider}/bind/status`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============ 钱包连接 ============
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 获取钱包连接消息
|
|
136
|
+
* @param {string} address - 钱包地址
|
|
137
|
+
*/
|
|
138
|
+
async getWalletMessage(address) {
|
|
139
|
+
return this.post('/wallet/message', {
|
|
140
|
+
address
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 验证钱包签名
|
|
146
|
+
* @param {string} address - 钱包地址
|
|
147
|
+
* @param {string} signature - 签名
|
|
148
|
+
* @param {string} message - 消息
|
|
149
|
+
*/
|
|
150
|
+
async verifyWalletSignature(address, signature, message) {
|
|
151
|
+
return this.post('/wallet/verify', {
|
|
152
|
+
address,
|
|
153
|
+
signature,
|
|
154
|
+
message
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 使用钱包登录
|
|
160
|
+
* @param {string} address - 钱包地址
|
|
161
|
+
* @param {string} signature - 签名
|
|
162
|
+
* @param {string} message - 消息
|
|
163
|
+
*/
|
|
164
|
+
async loginWithWallet(address, signature, message) {
|
|
165
|
+
return this.post('/wallet/login', {
|
|
166
|
+
address,
|
|
167
|
+
signature,
|
|
168
|
+
message
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 绑定钱包
|
|
174
|
+
* @param {string} address - 钱包地址
|
|
175
|
+
* @param {string} signature - 签名
|
|
176
|
+
* @param {string} message - 消息
|
|
177
|
+
*/
|
|
178
|
+
async bindWallet(address, signature, message) {
|
|
179
|
+
return this.post('/wallet/bind', {
|
|
180
|
+
address,
|
|
181
|
+
signature,
|
|
182
|
+
message
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 解绑钱包
|
|
188
|
+
* @param {string} address - 钱包地址
|
|
189
|
+
*/
|
|
190
|
+
async unbindWallet(address) {
|
|
191
|
+
return this.delete('/wallet/bind', {
|
|
192
|
+
address
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 获取已绑定钱包
|
|
198
|
+
*/
|
|
199
|
+
async getBoundWallets() {
|
|
200
|
+
return this.get('/wallet/list');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ============ SSO 单点登录 ============
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 获取 SSO 登录 URL
|
|
207
|
+
* @param {Object} options - 选项
|
|
208
|
+
*/
|
|
209
|
+
async getSSOUrl(options = {}) {
|
|
210
|
+
return this.get('/sso/url', options);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* SSO 登录验证
|
|
215
|
+
* @param {string} token - SSO Token
|
|
216
|
+
*/
|
|
217
|
+
async verifySSOToken(token) {
|
|
218
|
+
return this.post('/sso/verify', {
|
|
219
|
+
token
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* SSO 登出
|
|
225
|
+
*/
|
|
226
|
+
async ssoLogout() {
|
|
227
|
+
return this.post('/sso/logout');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 检查当前 OAuth 登录态
|
|
232
|
+
*/
|
|
233
|
+
async checkAuth() {
|
|
234
|
+
return this.get('/auth/check');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 登出当前 OAuth 会话
|
|
239
|
+
*/
|
|
240
|
+
async logout() {
|
|
241
|
+
return this.post('/logout');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ============ Token 管理 ============
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 刷新 Token
|
|
248
|
+
* @param {string} refreshToken - 刷新 Token
|
|
249
|
+
*/
|
|
250
|
+
async refreshToken(refreshToken) {
|
|
251
|
+
return this.post('/token/refresh', {
|
|
252
|
+
refresh_token: refreshToken
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 撤销 Token
|
|
258
|
+
* @param {string} token - Token
|
|
259
|
+
*/
|
|
260
|
+
async revokeToken(token) {
|
|
261
|
+
return this.post('/token/revoke', {
|
|
262
|
+
token
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 获取 Token 信息
|
|
268
|
+
* @param {string} token - Token
|
|
269
|
+
*/
|
|
270
|
+
async getTokenInfo(token) {
|
|
271
|
+
return this.post('/token/info', {
|
|
272
|
+
token
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ============ 授权管理 ============
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* 获取授权应用列表
|
|
280
|
+
*/
|
|
281
|
+
async getAuthorizedApps() {
|
|
282
|
+
return this.get('/apps');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 撤销应用授权
|
|
287
|
+
* @param {string} appId - 应用 ID
|
|
288
|
+
*/
|
|
289
|
+
async revokeAppAuthorization(appId) {
|
|
290
|
+
return this.delete(`/apps/${appId}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 创建默认实例
|
|
295
|
+
const oauthApi = new OauthApiClient();
|
|
296
|
+
const OAuthApiClient = OauthApiClient;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* OAuth SDK - 类型定义
|
|
300
|
+
*/
|
|
301
|
+
|
|
302
|
+
// 第三方登录提供商
|
|
303
|
+
const OAuthProvider = {
|
|
304
|
+
// 社交
|
|
305
|
+
GOOGLE: 'google',
|
|
306
|
+
FACEBOOK: 'facebook',
|
|
307
|
+
TWITTER: 'twitter',
|
|
308
|
+
GITHUB: 'github',
|
|
309
|
+
DISCORD: 'discord',
|
|
310
|
+
TELEGRAM: 'telegram',
|
|
311
|
+
WECHAT: 'wechat',
|
|
312
|
+
WEIBO: 'weibo',
|
|
313
|
+
QQ: 'qq',
|
|
314
|
+
APPLE: 'apple',
|
|
315
|
+
// 钱包
|
|
316
|
+
METAMASK: 'metamask',
|
|
317
|
+
WALLET_CONNECT: 'wallet_connect',
|
|
318
|
+
COINBASE_WALLET: 'coinbase_wallet',
|
|
319
|
+
PHANTOM: 'phantom',
|
|
320
|
+
// 企业
|
|
321
|
+
MICROSOFT: 'microsoft',
|
|
322
|
+
OKTA: 'okta',
|
|
323
|
+
AUTH0: 'auth0'
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// 连接状态
|
|
327
|
+
const ConnectionStatus = {
|
|
328
|
+
DISCONNECTED: 'disconnected',
|
|
329
|
+
CONNECTING: 'connecting',
|
|
330
|
+
CONNECTED: 'connected',
|
|
331
|
+
ERROR: 'error'
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// 账户绑定状态
|
|
335
|
+
const BindingStatus = {
|
|
336
|
+
NOT_BOUND: 'not_bound',
|
|
337
|
+
BOUND: 'bound',
|
|
338
|
+
PENDING: 'pending'
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// 授权范围
|
|
342
|
+
const OAuthScope = {
|
|
343
|
+
PROFILE: 'profile',
|
|
344
|
+
EMAIL: 'email',
|
|
345
|
+
OPENID: 'openid',
|
|
346
|
+
READ: 'read',
|
|
347
|
+
WRITE: 'write'
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* OAuth SDK - 国际化
|
|
352
|
+
* OAuth授权系统多语言支持
|
|
353
|
+
*/
|
|
354
|
+
|
|
355
|
+
const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko'];
|
|
356
|
+
const messages = {
|
|
357
|
+
zh: {
|
|
358
|
+
// 登录
|
|
359
|
+
login: '登录',
|
|
360
|
+
loginWith: '使用 {provider} 登录',
|
|
361
|
+
loginTo: '登录到',
|
|
362
|
+
signIn: '登录',
|
|
363
|
+
signUp: '注册',
|
|
364
|
+
signOut: '退出登录',
|
|
365
|
+
logout: '退出',
|
|
366
|
+
// 第三方
|
|
367
|
+
google: 'Google',
|
|
368
|
+
apple: 'Apple',
|
|
369
|
+
github: 'GitHub',
|
|
370
|
+
twitter: 'Twitter',
|
|
371
|
+
facebook: 'Facebook',
|
|
372
|
+
discord: 'Discord',
|
|
373
|
+
wechat: '微信',
|
|
374
|
+
qq: 'QQ',
|
|
375
|
+
// 授权
|
|
376
|
+
authorize: '授权',
|
|
377
|
+
authorization: '授权',
|
|
378
|
+
authorizing: '授权中...',
|
|
379
|
+
allowAccess: '允许访问',
|
|
380
|
+
denyAccess: '拒绝访问',
|
|
381
|
+
// 权限
|
|
382
|
+
permissions: '权限',
|
|
383
|
+
requestingPermissions: '请求以下权限',
|
|
384
|
+
readProfile: '读取基本资料',
|
|
385
|
+
readEmail: '读取邮箱地址',
|
|
386
|
+
writeData: '写入数据',
|
|
387
|
+
// 连接
|
|
388
|
+
connect: '连接',
|
|
389
|
+
connected: '已连接',
|
|
390
|
+
disconnect: '断开连接',
|
|
391
|
+
connectionRequired: '需要连接账户',
|
|
392
|
+
// 状态
|
|
393
|
+
loading: '加载中...',
|
|
394
|
+
redirecting: '跳转中...',
|
|
395
|
+
success: '授权成功',
|
|
396
|
+
failed: '授权失败',
|
|
397
|
+
error: '发生错误',
|
|
398
|
+
tryAgain: '重试',
|
|
399
|
+
// 账户
|
|
400
|
+
linkedAccounts: '已关联账户',
|
|
401
|
+
linkAccount: '关联账户',
|
|
402
|
+
unlinkAccount: '解除关联',
|
|
403
|
+
noLinkedAccounts: '暂无关联账户',
|
|
404
|
+
// 安全
|
|
405
|
+
secureLogin: '安全登录',
|
|
406
|
+
securedBy: '由 {provider} 提供安全保障',
|
|
407
|
+
// 协议
|
|
408
|
+
termsOfService: '服务条款',
|
|
409
|
+
privacyPolicy: '隐私政策',
|
|
410
|
+
byLoggingIn: '登录即表示您同意我们的',
|
|
411
|
+
and: '和',
|
|
412
|
+
// 返回
|
|
413
|
+
cancel: '取消',
|
|
414
|
+
back: '返回',
|
|
415
|
+
continueAs: '以 {name} 继续'
|
|
416
|
+
},
|
|
417
|
+
en: {
|
|
418
|
+
login: 'Login',
|
|
419
|
+
loginWith: 'Login with {provider}',
|
|
420
|
+
loginTo: 'Login to',
|
|
421
|
+
signIn: 'Sign In',
|
|
422
|
+
signUp: 'Sign Up',
|
|
423
|
+
signOut: 'Sign Out',
|
|
424
|
+
logout: 'Logout',
|
|
425
|
+
google: 'Google',
|
|
426
|
+
apple: 'Apple',
|
|
427
|
+
github: 'GitHub',
|
|
428
|
+
twitter: 'Twitter',
|
|
429
|
+
facebook: 'Facebook',
|
|
430
|
+
discord: 'Discord',
|
|
431
|
+
wechat: 'WeChat',
|
|
432
|
+
qq: 'QQ',
|
|
433
|
+
authorize: 'Authorize',
|
|
434
|
+
authorization: 'Authorization',
|
|
435
|
+
authorizing: 'Authorizing...',
|
|
436
|
+
allowAccess: 'Allow Access',
|
|
437
|
+
denyAccess: 'Deny Access',
|
|
438
|
+
permissions: 'Permissions',
|
|
439
|
+
requestingPermissions: 'Requesting the following permissions',
|
|
440
|
+
readProfile: 'Read your profile',
|
|
441
|
+
readEmail: 'Read your email',
|
|
442
|
+
writeData: 'Write data',
|
|
443
|
+
connect: 'Connect',
|
|
444
|
+
connected: 'Connected',
|
|
445
|
+
disconnect: 'Disconnect',
|
|
446
|
+
connectionRequired: 'Connection required',
|
|
447
|
+
loading: 'Loading...',
|
|
448
|
+
redirecting: 'Redirecting...',
|
|
449
|
+
success: 'Success',
|
|
450
|
+
failed: 'Failed',
|
|
451
|
+
error: 'Error',
|
|
452
|
+
tryAgain: 'Try Again',
|
|
453
|
+
linkedAccounts: 'Linked Accounts',
|
|
454
|
+
linkAccount: 'Link Account',
|
|
455
|
+
unlinkAccount: 'Unlink',
|
|
456
|
+
noLinkedAccounts: 'No linked accounts',
|
|
457
|
+
secureLogin: 'Secure Login',
|
|
458
|
+
securedBy: 'Secured by {provider}',
|
|
459
|
+
termsOfService: 'Terms of Service',
|
|
460
|
+
privacyPolicy: 'Privacy Policy',
|
|
461
|
+
byLoggingIn: 'By logging in, you agree to our',
|
|
462
|
+
and: 'and',
|
|
463
|
+
cancel: 'Cancel',
|
|
464
|
+
back: 'Back',
|
|
465
|
+
continueAs: 'Continue as {name}'
|
|
466
|
+
},
|
|
467
|
+
ja: {
|
|
468
|
+
login: 'ログイン',
|
|
469
|
+
loginWith: '{provider} でログイン',
|
|
470
|
+
loginTo: 'ログイン先',
|
|
471
|
+
signIn: 'サインイン',
|
|
472
|
+
signUp: 'サインアップ',
|
|
473
|
+
signOut: 'サインアウト',
|
|
474
|
+
logout: 'ログアウト',
|
|
475
|
+
google: 'Google',
|
|
476
|
+
apple: 'Apple',
|
|
477
|
+
github: 'GitHub',
|
|
478
|
+
twitter: 'Twitter',
|
|
479
|
+
facebook: 'Facebook',
|
|
480
|
+
discord: 'Discord',
|
|
481
|
+
wechat: 'WeChat',
|
|
482
|
+
qq: 'QQ',
|
|
483
|
+
authorize: '認可',
|
|
484
|
+
authorization: '認可',
|
|
485
|
+
authorizing: '認可中...',
|
|
486
|
+
allowAccess: 'アクセスを許可',
|
|
487
|
+
denyAccess: 'アクセスを拒否',
|
|
488
|
+
permissions: '権限',
|
|
489
|
+
requestingPermissions: '以下の権限を要求しています',
|
|
490
|
+
readProfile: 'プロフィールの読み取り',
|
|
491
|
+
readEmail: 'メールの読み取り',
|
|
492
|
+
writeData: 'データの書き込み',
|
|
493
|
+
connect: '接続',
|
|
494
|
+
connected: '接続済み',
|
|
495
|
+
disconnect: '切断',
|
|
496
|
+
connectionRequired: '接続が必要',
|
|
497
|
+
loading: '読み込み中...',
|
|
498
|
+
redirecting: 'リダイレクト中...',
|
|
499
|
+
success: '成功',
|
|
500
|
+
failed: '失敗',
|
|
501
|
+
error: 'エラー',
|
|
502
|
+
tryAgain: '再試行',
|
|
503
|
+
linkedAccounts: '連携済みアカウント',
|
|
504
|
+
linkAccount: 'アカウントを連携',
|
|
505
|
+
unlinkAccount: '連携解除',
|
|
506
|
+
noLinkedAccounts: '連携アカウントなし',
|
|
507
|
+
secureLogin: 'セキュアログイン',
|
|
508
|
+
securedBy: '{provider} によるセキュリティ',
|
|
509
|
+
termsOfService: '利用規約',
|
|
510
|
+
privacyPolicy: 'プライバシーポリシー',
|
|
511
|
+
byLoggingIn: 'ログインすると、以下に同意することになります:',
|
|
512
|
+
and: 'と',
|
|
513
|
+
cancel: 'キャンセル',
|
|
514
|
+
back: '戻る',
|
|
515
|
+
continueAs: '{name} として続行'
|
|
516
|
+
},
|
|
517
|
+
ko: {
|
|
518
|
+
login: '로그인',
|
|
519
|
+
loginWith: '{provider}로 로그인',
|
|
520
|
+
loginTo: '로그인 대상',
|
|
521
|
+
signIn: '로그인',
|
|
522
|
+
signUp: '가입하기',
|
|
523
|
+
signOut: '로그아웃',
|
|
524
|
+
logout: '로그아웃',
|
|
525
|
+
google: 'Google',
|
|
526
|
+
apple: 'Apple',
|
|
527
|
+
github: 'GitHub',
|
|
528
|
+
twitter: 'Twitter',
|
|
529
|
+
facebook: 'Facebook',
|
|
530
|
+
discord: 'Discord',
|
|
531
|
+
wechat: 'WeChat',
|
|
532
|
+
qq: 'QQ',
|
|
533
|
+
authorize: '승인',
|
|
534
|
+
authorization: '승인',
|
|
535
|
+
authorizing: '승인 중...',
|
|
536
|
+
allowAccess: '접근 허용',
|
|
537
|
+
denyAccess: '접근 거부',
|
|
538
|
+
permissions: '권한',
|
|
539
|
+
requestingPermissions: '다음 권한을 요청합니다',
|
|
540
|
+
readProfile: '프로필 읽기',
|
|
541
|
+
readEmail: '이메일 읽기',
|
|
542
|
+
writeData: '데이터 쓰기',
|
|
543
|
+
connect: '연결',
|
|
544
|
+
connected: '연결됨',
|
|
545
|
+
disconnect: '연결 해제',
|
|
546
|
+
connectionRequired: '연결 필요',
|
|
547
|
+
loading: '로딩 중...',
|
|
548
|
+
redirecting: '리다이렉트 중...',
|
|
549
|
+
success: '성공',
|
|
550
|
+
failed: '실패',
|
|
551
|
+
error: '오류',
|
|
552
|
+
tryAgain: '다시 시도',
|
|
553
|
+
linkedAccounts: '연결된 계정',
|
|
554
|
+
linkAccount: '계정 연결',
|
|
555
|
+
unlinkAccount: '연결 해제',
|
|
556
|
+
noLinkedAccounts: '연결된 계정 없음',
|
|
557
|
+
secureLogin: '보안 로그인',
|
|
558
|
+
securedBy: '{provider}에 의해 보안됨',
|
|
559
|
+
termsOfService: '서비스 약관',
|
|
560
|
+
privacyPolicy: '개인정보 처리방침',
|
|
561
|
+
byLoggingIn: '로그인하면 다음에 동의하는 것입니다:',
|
|
562
|
+
and: '및',
|
|
563
|
+
cancel: '취소',
|
|
564
|
+
back: '뒤로',
|
|
565
|
+
continueAs: '{name}(으)로 계속'
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
let currentLanguage = 'zh';
|
|
569
|
+
function setLanguage(lang) {
|
|
570
|
+
if (SUPPORTED_LANGUAGES.includes(lang)) currentLanguage = lang;
|
|
571
|
+
}
|
|
572
|
+
function getLanguage() {
|
|
573
|
+
return currentLanguage;
|
|
574
|
+
}
|
|
575
|
+
function t(key, params = {}) {
|
|
576
|
+
let text = (messages[currentLanguage] || messages.en)[key] || key;
|
|
577
|
+
Object.entries(params).forEach(([k, v]) => {
|
|
578
|
+
text = text.replace(new RegExp(`\\{${k}\\}`, 'g'), v);
|
|
579
|
+
});
|
|
580
|
+
return text;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* OAuth SDK - React Hooks
|
|
585
|
+
* OAuth授权系统相关的状态管理
|
|
586
|
+
*/
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* OAuth登录
|
|
591
|
+
*/
|
|
592
|
+
function useOAuthLogin() {
|
|
593
|
+
const [loading, setLoading] = React.useState(false);
|
|
594
|
+
const [error, setError] = React.useState(null);
|
|
595
|
+
const login = React.useCallback(async (provider, options = {}) => {
|
|
596
|
+
try {
|
|
597
|
+
setLoading(true);
|
|
598
|
+
setError(null);
|
|
599
|
+
const {
|
|
600
|
+
redirectUri,
|
|
601
|
+
scope,
|
|
602
|
+
state
|
|
603
|
+
} = options;
|
|
604
|
+
|
|
605
|
+
// 获取授权URL
|
|
606
|
+
const response = await oauthApi.getAuthUrl({
|
|
607
|
+
provider,
|
|
608
|
+
redirectUri: redirectUri || window.location.origin + '/oauth/callback',
|
|
609
|
+
scope,
|
|
610
|
+
state
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// 打开授权窗口或跳转
|
|
614
|
+
if (options.popup) {
|
|
615
|
+
return openPopup(response.authUrl, provider);
|
|
616
|
+
} else {
|
|
617
|
+
window.location.href = response.authUrl;
|
|
618
|
+
}
|
|
619
|
+
} catch (err) {
|
|
620
|
+
setError(err.message);
|
|
621
|
+
throw err;
|
|
622
|
+
} finally {
|
|
623
|
+
setLoading(false);
|
|
624
|
+
}
|
|
625
|
+
}, []);
|
|
626
|
+
return {
|
|
627
|
+
login,
|
|
628
|
+
loading,
|
|
629
|
+
error
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* OAuth回调处理
|
|
635
|
+
*/
|
|
636
|
+
function useOAuthCallback() {
|
|
637
|
+
const [status, setStatus] = React.useState('idle'); // idle, loading, success, error
|
|
638
|
+
const [user, setUser] = React.useState(null);
|
|
639
|
+
const [error, setError] = React.useState(null);
|
|
640
|
+
const handleCallback = React.useCallback(async (code, state, provider) => {
|
|
641
|
+
try {
|
|
642
|
+
setStatus('loading');
|
|
643
|
+
setError(null);
|
|
644
|
+
const response = await oauthApi.handleCallback({
|
|
645
|
+
code,
|
|
646
|
+
state,
|
|
647
|
+
provider
|
|
648
|
+
});
|
|
649
|
+
setUser(response.user);
|
|
650
|
+
setStatus('success');
|
|
651
|
+
return response;
|
|
652
|
+
} catch (err) {
|
|
653
|
+
setError(err.message);
|
|
654
|
+
setStatus('error');
|
|
655
|
+
throw err;
|
|
656
|
+
}
|
|
657
|
+
}, []);
|
|
658
|
+
|
|
659
|
+
// 自动处理URL参数
|
|
660
|
+
React.useEffect(() => {
|
|
661
|
+
const params = new URLSearchParams(window.location.search);
|
|
662
|
+
const code = params.get('code');
|
|
663
|
+
const state = params.get('state');
|
|
664
|
+
const provider = params.get('provider');
|
|
665
|
+
if (code) {
|
|
666
|
+
handleCallback(code, state, provider);
|
|
667
|
+
}
|
|
668
|
+
}, [handleCallback]);
|
|
669
|
+
return {
|
|
670
|
+
status,
|
|
671
|
+
user,
|
|
672
|
+
error,
|
|
673
|
+
handleCallback
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* 已关联账户
|
|
679
|
+
*/
|
|
680
|
+
function useLinkedAccounts() {
|
|
681
|
+
const [accounts, setAccounts] = React.useState([]);
|
|
682
|
+
const [loading, setLoading] = React.useState(true);
|
|
683
|
+
const [error, setError] = React.useState(null);
|
|
684
|
+
const fetchAccounts = React.useCallback(async () => {
|
|
685
|
+
try {
|
|
686
|
+
setLoading(true);
|
|
687
|
+
const response = await oauthApi.getLinkedAccounts();
|
|
688
|
+
setAccounts(response.data || []);
|
|
689
|
+
setError(null);
|
|
690
|
+
} catch (err) {
|
|
691
|
+
setError(err.message);
|
|
692
|
+
} finally {
|
|
693
|
+
setLoading(false);
|
|
694
|
+
}
|
|
695
|
+
}, []);
|
|
696
|
+
React.useEffect(() => {
|
|
697
|
+
fetchAccounts();
|
|
698
|
+
}, [fetchAccounts]);
|
|
699
|
+
const linkAccount = React.useCallback(async provider => {
|
|
700
|
+
const response = await oauthApi.getAuthUrl({
|
|
701
|
+
provider,
|
|
702
|
+
redirectUri: window.location.origin + '/oauth/link-callback',
|
|
703
|
+
isLink: true
|
|
704
|
+
});
|
|
705
|
+
window.location.href = response.authUrl;
|
|
706
|
+
}, []);
|
|
707
|
+
const unlinkAccount = React.useCallback(async provider => {
|
|
708
|
+
try {
|
|
709
|
+
await oauthApi.unlinkAccount(provider);
|
|
710
|
+
setAccounts(prev => prev.filter(a => a.provider !== provider));
|
|
711
|
+
} catch (err) {
|
|
712
|
+
console.error('Unlink error:', err);
|
|
713
|
+
throw err;
|
|
714
|
+
}
|
|
715
|
+
}, []);
|
|
716
|
+
const isLinked = React.useCallback(provider => {
|
|
717
|
+
return accounts.some(a => a.provider === provider);
|
|
718
|
+
}, [accounts]);
|
|
719
|
+
return {
|
|
720
|
+
accounts,
|
|
721
|
+
loading,
|
|
722
|
+
error,
|
|
723
|
+
linkAccount,
|
|
724
|
+
unlinkAccount,
|
|
725
|
+
isLinked,
|
|
726
|
+
refresh: fetchAccounts
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* OAuth状态
|
|
732
|
+
*/
|
|
733
|
+
function useOAuthState() {
|
|
734
|
+
const [isAuthenticated, setIsAuthenticated] = React.useState(false);
|
|
735
|
+
const [user, setUser] = React.useState(null);
|
|
736
|
+
const [loading, setLoading] = React.useState(true);
|
|
737
|
+
const checkAuth = React.useCallback(async () => {
|
|
738
|
+
try {
|
|
739
|
+
setLoading(true);
|
|
740
|
+
const response = await oauthApi.checkAuth();
|
|
741
|
+
setIsAuthenticated(response.isAuthenticated);
|
|
742
|
+
setUser(response.user);
|
|
743
|
+
} catch (err) {
|
|
744
|
+
setIsAuthenticated(false);
|
|
745
|
+
setUser(null);
|
|
746
|
+
} finally {
|
|
747
|
+
setLoading(false);
|
|
748
|
+
}
|
|
749
|
+
}, []);
|
|
750
|
+
React.useEffect(() => {
|
|
751
|
+
checkAuth();
|
|
752
|
+
}, [checkAuth]);
|
|
753
|
+
const logout = React.useCallback(async () => {
|
|
754
|
+
try {
|
|
755
|
+
await oauthApi.logout();
|
|
756
|
+
setIsAuthenticated(false);
|
|
757
|
+
setUser(null);
|
|
758
|
+
} catch (err) {
|
|
759
|
+
console.error('Logout error:', err);
|
|
760
|
+
}
|
|
761
|
+
}, []);
|
|
762
|
+
return {
|
|
763
|
+
isAuthenticated,
|
|
764
|
+
user,
|
|
765
|
+
loading,
|
|
766
|
+
logout,
|
|
767
|
+
refresh: checkAuth
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* 可用的OAuth提供商
|
|
773
|
+
*/
|
|
774
|
+
function useOAuthProviders() {
|
|
775
|
+
const [providers, setProviders] = React.useState([]);
|
|
776
|
+
const [loading, setLoading] = React.useState(true);
|
|
777
|
+
const fetchProviders = React.useCallback(async () => {
|
|
778
|
+
try {
|
|
779
|
+
setLoading(true);
|
|
780
|
+
const response = await oauthApi.getProviders();
|
|
781
|
+
setProviders(response.data || []);
|
|
782
|
+
} catch (err) {
|
|
783
|
+
console.error('Get providers error:', err);
|
|
784
|
+
} finally {
|
|
785
|
+
setLoading(false);
|
|
786
|
+
}
|
|
787
|
+
}, []);
|
|
788
|
+
React.useEffect(() => {
|
|
789
|
+
fetchProviders();
|
|
790
|
+
}, [fetchProviders]);
|
|
791
|
+
return {
|
|
792
|
+
providers,
|
|
793
|
+
loading
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* 弹窗登录
|
|
799
|
+
*/
|
|
800
|
+
function openPopup(url, name) {
|
|
801
|
+
const width = 500;
|
|
802
|
+
const height = 600;
|
|
803
|
+
const left = (window.innerWidth - width) / 2 + window.screenX;
|
|
804
|
+
const top = (window.innerHeight - height) / 2 + window.screenY;
|
|
805
|
+
const popup = window.open(url, name, `width=${width},height=${height},left=${left},top=${top}`);
|
|
806
|
+
return new Promise((resolve, reject) => {
|
|
807
|
+
const checkClosed = setInterval(() => {
|
|
808
|
+
if (!popup || popup.closed) {
|
|
809
|
+
clearInterval(checkClosed);
|
|
810
|
+
reject(new Error('Login window closed'));
|
|
811
|
+
}
|
|
812
|
+
}, 500);
|
|
813
|
+
|
|
814
|
+
// 监听消息
|
|
815
|
+
const handleMessage = event => {
|
|
816
|
+
if (event.origin !== window.location.origin) return;
|
|
817
|
+
if (event.data?.type === 'oauth_callback') {
|
|
818
|
+
clearInterval(checkClosed);
|
|
819
|
+
window.removeEventListener('message', handleMessage);
|
|
820
|
+
popup?.close();
|
|
821
|
+
if (event.data.error) {
|
|
822
|
+
reject(new Error(event.data.error));
|
|
823
|
+
} else {
|
|
824
|
+
resolve(event.data.result);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
window.addEventListener('message', handleMessage);
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* OAuth SDK - React 组件
|
|
834
|
+
* OAuth授权系统可视化组件
|
|
835
|
+
*/
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
// 提供商图标
|
|
839
|
+
const providerIcons = {
|
|
840
|
+
google: '🔵',
|
|
841
|
+
apple: '🍎',
|
|
842
|
+
github: '⚫',
|
|
843
|
+
twitter: '🐦',
|
|
844
|
+
facebook: '📘',
|
|
845
|
+
discord: '🎮',
|
|
846
|
+
wechat: '💬',
|
|
847
|
+
qq: '🐧'
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
// 提供商品牌色
|
|
851
|
+
const providerColors = {
|
|
852
|
+
google: '#4285f4',
|
|
853
|
+
apple: '#000000',
|
|
854
|
+
github: '#24292e',
|
|
855
|
+
twitter: '#1da1f2',
|
|
856
|
+
facebook: '#1877f2',
|
|
857
|
+
discord: '#5865f2',
|
|
858
|
+
wechat: '#07c160',
|
|
859
|
+
qq: '#12b7f5'
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* 第三方登录按钮
|
|
864
|
+
*/
|
|
865
|
+
function OAuthButton({
|
|
866
|
+
provider,
|
|
867
|
+
variant = 'default',
|
|
868
|
+
onSuccess,
|
|
869
|
+
onError
|
|
870
|
+
}) {
|
|
871
|
+
const {
|
|
872
|
+
login,
|
|
873
|
+
loading
|
|
874
|
+
} = useOAuthLogin();
|
|
875
|
+
const handleClick = React.useCallback(async () => {
|
|
876
|
+
try {
|
|
877
|
+
const result = await login(provider, {
|
|
878
|
+
popup: true
|
|
879
|
+
});
|
|
880
|
+
onSuccess?.(result);
|
|
881
|
+
} catch (err) {
|
|
882
|
+
onError?.(err);
|
|
883
|
+
}
|
|
884
|
+
}, [login, provider, onSuccess, onError]);
|
|
885
|
+
const icon = providerIcons[provider] || '🔐';
|
|
886
|
+
const color = providerColors[provider];
|
|
887
|
+
return /*#__PURE__*/React.createElement("button", {
|
|
888
|
+
className: `eco-oauth-btn eco-oauth-btn-${variant} eco-oauth-btn-${provider}`,
|
|
889
|
+
onClick: handleClick,
|
|
890
|
+
disabled: loading,
|
|
891
|
+
style: variant === 'brand' ? {
|
|
892
|
+
backgroundColor: color
|
|
893
|
+
} : undefined
|
|
894
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
895
|
+
className: "eco-oauth-btn-icon"
|
|
896
|
+
}, icon), /*#__PURE__*/React.createElement("span", {
|
|
897
|
+
className: "eco-oauth-btn-text"
|
|
898
|
+
}, loading ? t('loading') : t('loginWith', {
|
|
899
|
+
provider: t(provider)
|
|
900
|
+
})));
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* 登录按钮组
|
|
905
|
+
*/
|
|
906
|
+
function OAuthButtonGroup({
|
|
907
|
+
providers = ['google', 'github', 'twitter'],
|
|
908
|
+
variant = 'default',
|
|
909
|
+
onSuccess,
|
|
910
|
+
onError
|
|
911
|
+
}) {
|
|
912
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
913
|
+
className: "eco-oauth-btn-group"
|
|
914
|
+
}, providers.map(provider => /*#__PURE__*/React.createElement(OAuthButton, {
|
|
915
|
+
key: provider,
|
|
916
|
+
provider: provider,
|
|
917
|
+
variant: variant,
|
|
918
|
+
onSuccess: onSuccess,
|
|
919
|
+
onError: onError
|
|
920
|
+
})));
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* 图标按钮组
|
|
925
|
+
*/
|
|
926
|
+
function OAuthIconGroup({
|
|
927
|
+
providers = ['google', 'github', 'twitter'],
|
|
928
|
+
onSuccess,
|
|
929
|
+
onError
|
|
930
|
+
}) {
|
|
931
|
+
const {
|
|
932
|
+
login,
|
|
933
|
+
loading
|
|
934
|
+
} = useOAuthLogin();
|
|
935
|
+
const handleClick = React.useCallback(async provider => {
|
|
936
|
+
try {
|
|
937
|
+
const result = await login(provider, {
|
|
938
|
+
popup: true
|
|
939
|
+
});
|
|
940
|
+
onSuccess?.(result);
|
|
941
|
+
} catch (err) {
|
|
942
|
+
onError?.(err);
|
|
943
|
+
}
|
|
944
|
+
}, [login, onSuccess, onError]);
|
|
945
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
946
|
+
className: "eco-oauth-icon-group"
|
|
947
|
+
}, providers.map(provider => /*#__PURE__*/React.createElement("button", {
|
|
948
|
+
key: provider,
|
|
949
|
+
className: `eco-oauth-icon-btn eco-oauth-icon-${provider}`,
|
|
950
|
+
onClick: () => handleClick(provider),
|
|
951
|
+
disabled: loading,
|
|
952
|
+
title: t(provider),
|
|
953
|
+
style: {
|
|
954
|
+
backgroundColor: providerColors[provider]
|
|
955
|
+
}
|
|
956
|
+
}, providerIcons[provider] || '🔐')));
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* OAuth回调页面
|
|
961
|
+
*/
|
|
962
|
+
function OAuthCallbackPage({
|
|
963
|
+
onSuccess,
|
|
964
|
+
onError
|
|
965
|
+
}) {
|
|
966
|
+
const {
|
|
967
|
+
status,
|
|
968
|
+
user,
|
|
969
|
+
error
|
|
970
|
+
} = useOAuthCallback();
|
|
971
|
+
if (status === 'loading') {
|
|
972
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
973
|
+
className: "eco-oauth-callback"
|
|
974
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
975
|
+
className: "eco-oauth-callback-loading"
|
|
976
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
977
|
+
className: "eco-oauth-spinner"
|
|
978
|
+
}), /*#__PURE__*/React.createElement("span", null, t('authorizing'))));
|
|
979
|
+
}
|
|
980
|
+
if (status === 'error') {
|
|
981
|
+
onError?.(error);
|
|
982
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
983
|
+
className: "eco-oauth-callback"
|
|
984
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
985
|
+
className: "eco-oauth-callback-error"
|
|
986
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
987
|
+
className: "eco-oauth-callback-icon"
|
|
988
|
+
}, "\u274C"), /*#__PURE__*/React.createElement("h3", null, t('failed')), /*#__PURE__*/React.createElement("p", null, error), /*#__PURE__*/React.createElement("button", {
|
|
989
|
+
onClick: () => window.close()
|
|
990
|
+
}, t('back'))));
|
|
991
|
+
}
|
|
992
|
+
if (status === 'success') {
|
|
993
|
+
onSuccess?.(user);
|
|
994
|
+
|
|
995
|
+
// 如果在弹窗中,发送消息给父窗口
|
|
996
|
+
if (window.opener) {
|
|
997
|
+
window.opener.postMessage({
|
|
998
|
+
type: 'oauth_callback',
|
|
999
|
+
result: {
|
|
1000
|
+
user
|
|
1001
|
+
}
|
|
1002
|
+
}, window.location.origin);
|
|
1003
|
+
}
|
|
1004
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1005
|
+
className: "eco-oauth-callback"
|
|
1006
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1007
|
+
className: "eco-oauth-callback-success"
|
|
1008
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1009
|
+
className: "eco-oauth-callback-icon"
|
|
1010
|
+
}, "\u2705"), /*#__PURE__*/React.createElement("h3", null, t('success')), /*#__PURE__*/React.createElement("p", null, t('continueAs', {
|
|
1011
|
+
name: user?.name
|
|
1012
|
+
}))));
|
|
1013
|
+
}
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* 已关联账户列表
|
|
1019
|
+
*/
|
|
1020
|
+
function LinkedAccountsList() {
|
|
1021
|
+
const {
|
|
1022
|
+
accounts,
|
|
1023
|
+
loading,
|
|
1024
|
+
unlinkAccount,
|
|
1025
|
+
isLinked
|
|
1026
|
+
} = useLinkedAccounts();
|
|
1027
|
+
const {
|
|
1028
|
+
providers
|
|
1029
|
+
} = useOAuthProviders();
|
|
1030
|
+
if (loading) {
|
|
1031
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1032
|
+
className: "eco-oauth-linked eco-oauth-loading"
|
|
1033
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1034
|
+
className: "eco-oauth-spinner"
|
|
1035
|
+
}));
|
|
1036
|
+
}
|
|
1037
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1038
|
+
className: "eco-oauth-linked"
|
|
1039
|
+
}, /*#__PURE__*/React.createElement("h4", null, t('linkedAccounts')), accounts.length === 0 ? /*#__PURE__*/React.createElement("div", {
|
|
1040
|
+
className: "eco-oauth-linked-empty"
|
|
1041
|
+
}, t('noLinkedAccounts')) : /*#__PURE__*/React.createElement("div", {
|
|
1042
|
+
className: "eco-oauth-linked-list"
|
|
1043
|
+
}, accounts.map(account => /*#__PURE__*/React.createElement("div", {
|
|
1044
|
+
key: account.provider,
|
|
1045
|
+
className: "eco-oauth-linked-item"
|
|
1046
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1047
|
+
className: "eco-oauth-linked-icon",
|
|
1048
|
+
style: {
|
|
1049
|
+
backgroundColor: providerColors[account.provider]
|
|
1050
|
+
}
|
|
1051
|
+
}, providerIcons[account.provider]), /*#__PURE__*/React.createElement("div", {
|
|
1052
|
+
className: "eco-oauth-linked-info"
|
|
1053
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1054
|
+
className: "eco-oauth-linked-name"
|
|
1055
|
+
}, t(account.provider)), /*#__PURE__*/React.createElement("span", {
|
|
1056
|
+
className: "eco-oauth-linked-email"
|
|
1057
|
+
}, account.email)), /*#__PURE__*/React.createElement("button", {
|
|
1058
|
+
className: "eco-oauth-unlink-btn",
|
|
1059
|
+
onClick: () => unlinkAccount(account.provider)
|
|
1060
|
+
}, t('unlinkAccount'))))), /*#__PURE__*/React.createElement("div", {
|
|
1061
|
+
className: "eco-oauth-link-more"
|
|
1062
|
+
}, /*#__PURE__*/React.createElement("h5", null, t('linkAccount')), /*#__PURE__*/React.createElement("div", {
|
|
1063
|
+
className: "eco-oauth-link-options"
|
|
1064
|
+
}, providers.filter(p => !isLinked(p.id)).map(p => /*#__PURE__*/React.createElement(OAuthButton, {
|
|
1065
|
+
key: p.id,
|
|
1066
|
+
provider: p.id,
|
|
1067
|
+
variant: "outline"
|
|
1068
|
+
})))));
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* 登录模态框
|
|
1073
|
+
*/
|
|
1074
|
+
function OAuthLoginModal({
|
|
1075
|
+
isOpen,
|
|
1076
|
+
onClose,
|
|
1077
|
+
onSuccess
|
|
1078
|
+
}) {
|
|
1079
|
+
if (!isOpen) return null;
|
|
1080
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1081
|
+
className: "eco-oauth-modal"
|
|
1082
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1083
|
+
className: "eco-oauth-modal-overlay",
|
|
1084
|
+
onClick: onClose
|
|
1085
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
1086
|
+
className: "eco-oauth-modal-content"
|
|
1087
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
1088
|
+
className: "eco-oauth-modal-close",
|
|
1089
|
+
onClick: onClose
|
|
1090
|
+
}, "\xD7"), /*#__PURE__*/React.createElement("h2", null, t('login')), /*#__PURE__*/React.createElement(OAuthButtonGroup, {
|
|
1091
|
+
providers: ['google', 'github', 'twitter'],
|
|
1092
|
+
variant: "brand",
|
|
1093
|
+
onSuccess: result => {
|
|
1094
|
+
onSuccess?.(result);
|
|
1095
|
+
onClose();
|
|
1096
|
+
}
|
|
1097
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
1098
|
+
className: "eco-oauth-modal-divider"
|
|
1099
|
+
}, /*#__PURE__*/React.createElement("span", null, "\u6216")), /*#__PURE__*/React.createElement(OAuthIconGroup, {
|
|
1100
|
+
providers: ['facebook', 'discord', 'apple'],
|
|
1101
|
+
onSuccess: result => {
|
|
1102
|
+
onSuccess?.(result);
|
|
1103
|
+
onClose();
|
|
1104
|
+
}
|
|
1105
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
1106
|
+
className: "eco-oauth-modal-footer"
|
|
1107
|
+
}, /*#__PURE__*/React.createElement("span", null, t('byLoggingIn')), /*#__PURE__*/React.createElement("a", {
|
|
1108
|
+
href: "/terms"
|
|
1109
|
+
}, t('termsOfService')), /*#__PURE__*/React.createElement("span", null, t('and')), /*#__PURE__*/React.createElement("a", {
|
|
1110
|
+
href: "/privacy"
|
|
1111
|
+
}, t('privacyPolicy')))));
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* @quantabit/oauth-sdk
|
|
1116
|
+
* OAuth System SDK - Full Version
|
|
1117
|
+
*/
|
|
1118
|
+
|
|
1119
|
+
const getAuthUrl = (...args) => oauthApi.getAuthUrl(...args);
|
|
1120
|
+
const handleCallback = (...args) => oauthApi.handleCallback(...args);
|
|
1121
|
+
const checkAuth = (...args) => oauthApi.checkAuth(...args);
|
|
1122
|
+
const logout = (...args) => oauthApi.logout(...args);
|
|
1123
|
+
const getLinkedAccounts = (...args) => oauthApi.getLinkedAccounts(...args);
|
|
1124
|
+
|
|
1125
|
+
exports.BindingStatus = BindingStatus;
|
|
1126
|
+
exports.ConnectionStatus = ConnectionStatus;
|
|
1127
|
+
exports.LinkedAccountsList = LinkedAccountsList;
|
|
1128
|
+
exports.OAuthApiClient = OAuthApiClient;
|
|
1129
|
+
exports.OAuthButton = OAuthButton;
|
|
1130
|
+
exports.OAuthButtonGroup = OAuthButtonGroup;
|
|
1131
|
+
exports.OAuthCallbackPage = OAuthCallbackPage;
|
|
1132
|
+
exports.OAuthIconGroup = OAuthIconGroup;
|
|
1133
|
+
exports.OAuthLoginModal = OAuthLoginModal;
|
|
1134
|
+
exports.OAuthProvider = OAuthProvider;
|
|
1135
|
+
exports.OAuthScope = OAuthScope;
|
|
1136
|
+
exports.SUPPORTED_LANGUAGES = SUPPORTED_LANGUAGES;
|
|
1137
|
+
exports.checkAuth = checkAuth;
|
|
1138
|
+
exports.getAuthUrl = getAuthUrl;
|
|
1139
|
+
exports.getLanguage = getLanguage;
|
|
1140
|
+
exports.getLinkedAccounts = getLinkedAccounts;
|
|
1141
|
+
exports.handleCallback = handleCallback;
|
|
1142
|
+
exports.logout = logout;
|
|
1143
|
+
exports.messages = messages;
|
|
1144
|
+
exports.oauthApi = oauthApi;
|
|
1145
|
+
exports.setLanguage = setLanguage;
|
|
1146
|
+
exports.t = t;
|
|
1147
|
+
exports.useLinkedAccounts = useLinkedAccounts;
|
|
1148
|
+
exports.useOAuthCallback = useOAuthCallback;
|
|
1149
|
+
exports.useOAuthLogin = useOAuthLogin;
|
|
1150
|
+
exports.useOAuthProviders = useOAuthProviders;
|
|
1151
|
+
exports.useOAuthState = useOAuthState;
|
|
1152
|
+
//# sourceMappingURL=index.cjs.map
|