mirror of
https://github.com/doocs/cose.git
synced 2026-08-30 17:59:27 +08:00
refactor: add detection module (#163)
This commit is contained in:
@@ -15,7 +15,8 @@
|
||||
"lint": "web-ext lint --source-dir ./dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cose/core": "workspace:*"
|
||||
"@cose/core": "workspace:*",
|
||||
"@cose/detection": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cac": "^6.7.14",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+3
-373
@@ -1,373 +1,3 @@
|
||||
/**
|
||||
* @cose/detection - Platform login detection module
|
||||
*
|
||||
* This package provides login detection configurations for all supported platforms.
|
||||
* Each config includes:
|
||||
* - api: The API endpoint to check login status
|
||||
* - method: HTTP method (GET/POST)
|
||||
* - checkLogin: Function to determine if user is logged in from response
|
||||
* - getUserInfo: Function to extract username and avatar from response
|
||||
*/
|
||||
|
||||
// CSDN
|
||||
export const CSDNLoginConfig = {
|
||||
api: 'https://passport.csdn.net/v1/api/info',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.data?.userId,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickName,
|
||||
avatar: response?.data?.avatarUrl,
|
||||
}),
|
||||
}
|
||||
|
||||
// 掘金
|
||||
export const JuejinLoginConfig = {
|
||||
api: 'https://api.juejin.cn/user_api/v1/user/get',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.err_no === 0 && response?.data?.user_id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.user_name,
|
||||
avatar: response?.data?.avatar_large,
|
||||
}),
|
||||
}
|
||||
|
||||
// 微信公众号
|
||||
export const WechatLoginConfig = {
|
||||
api: 'https://mp.weixin.qq.com/cgi-bin/logininfo?action=logininfo&lang=zh_CN',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.base_resp?.ret === 0 && response?.user_name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.nick_name || response?.user_name,
|
||||
avatar: response?.head_img,
|
||||
}),
|
||||
}
|
||||
|
||||
// 知乎
|
||||
export const ZhihuLoginConfig = {
|
||||
api: 'https://www.zhihu.com/api/v4/me',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.name,
|
||||
avatar: response?.avatar_url,
|
||||
}),
|
||||
}
|
||||
|
||||
// 头条号
|
||||
export const ToutiaoLoginConfig = {
|
||||
api: 'https://mp.toutiao.com/mp/agw/creator_center/user_info?app_id=1231',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 0 && response?.name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.name,
|
||||
avatar: response?.avatar_url,
|
||||
}),
|
||||
}
|
||||
|
||||
// SegmentFault
|
||||
export const SegmentFaultLoginConfig = {
|
||||
api: 'https://segmentfault.com/gateway/user/me',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.status === 0 && response?.data?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.name,
|
||||
avatar: response?.data?.avatar_url,
|
||||
}),
|
||||
}
|
||||
|
||||
// 博客园
|
||||
export const CnblogsLoginConfig = {
|
||||
api: 'https://www.cnblogs.com/api/users/current',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.UserId,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.DisplayName,
|
||||
avatar: response?.Avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 开源中国
|
||||
export const OSChinaLoginConfig = {
|
||||
api: 'https://www.oschina.net/action/user/detail?format=json',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.name,
|
||||
avatar: response?.portrait,
|
||||
}),
|
||||
}
|
||||
|
||||
// 51CTO
|
||||
export const CTO51LoginConfig = {
|
||||
api: 'https://home.51cto.com/api/user/info/getUserBasicInfo',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 200 && response?.data?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.headpic,
|
||||
}),
|
||||
}
|
||||
|
||||
// InfoQ
|
||||
export const InfoQLoginConfig = {
|
||||
api: 'https://www.infoq.cn/public/v1/my/menu',
|
||||
method: 'POST',
|
||||
body: JSON.stringify({}),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
checkLogin: (response) => response?.code === 0 && response?.data?.username,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 简书
|
||||
export const JianshuLoginConfig = {
|
||||
api: 'https://www.jianshu.com/settings/basic',
|
||||
method: 'GET',
|
||||
isHtml: true,
|
||||
checkLogin: (html) => !html.includes('登录'),
|
||||
getUserInfo: () => ({ username: null, avatar: null }),
|
||||
}
|
||||
|
||||
// 百家号
|
||||
export const BaijiahaoLoginConfig = {
|
||||
api: 'https://baijiahao.baidu.com/builder/app/appinfo',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.errno === 0 && response?.data?.user?.name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.user?.name,
|
||||
avatar: response?.data?.user?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 网易号
|
||||
export const WangyihaoLoginConfig = {
|
||||
api: 'https://mp.163.com/api/account/info',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 1000 && response?.data?.nickname,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.headImg,
|
||||
}),
|
||||
}
|
||||
|
||||
// 腾讯云开发者社区
|
||||
export const TencentCloudLoginConfig = {
|
||||
api: 'https://cloud.tencent.com/developer/api/user/session',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 0 && response?.data?.uin,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// Medium
|
||||
export const MediumLoginConfig = {
|
||||
api: 'https://medium.com/me/stats?format=json',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.success,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.payload?.user?.name,
|
||||
avatar: response?.payload?.user?.imageId ? `https://miro.medium.com/v2/resize:fill:64:64/${response.payload.user.imageId}` : null,
|
||||
}),
|
||||
}
|
||||
|
||||
// 少数派
|
||||
export const SspaiLoginConfig = {
|
||||
api: 'https://sspai.com/api/v1/user/info/get',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.error === 0 && response?.data?.nickname,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 搜狐号
|
||||
export const SohuLoginConfig = {
|
||||
api: 'https://mp.sohu.com/mpbp/bp/account/list',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.success === true && response?.data?.total > 0,
|
||||
getUserInfo: (response) => {
|
||||
const account = response?.data?.data?.[0]?.accounts?.[0]
|
||||
let avatar = account?.avatar || ''
|
||||
if (avatar && avatar.startsWith('//')) {
|
||||
avatar = 'https:' + avatar
|
||||
}
|
||||
return {
|
||||
username: account?.nickName,
|
||||
avatar: avatar,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// B站
|
||||
export const BilibiliLoginConfig = {
|
||||
api: 'https://api.bilibili.com/x/web-interface/nav',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 0 && response?.data?.isLogin === true,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.uname,
|
||||
avatar: response?.data?.face,
|
||||
}),
|
||||
}
|
||||
|
||||
// 微博
|
||||
export const WeiboLoginConfig = {
|
||||
api: 'https://card.weibo.com/article/v3/aj/editor/draft/list?page=1&pagesize=1',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 100000,
|
||||
getUserInfo: () => ({ username: null, avatar: null }),
|
||||
}
|
||||
|
||||
// 阿里云开发者社区
|
||||
export const AliyunLoginConfig = {
|
||||
api: 'https://developer.aliyun.com/developer/api/my/user/getUser',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.success && response?.data?.accountId,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nick,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 华为云开发者博客
|
||||
export const HuaweiCloudLoginConfig = {
|
||||
api: 'https://bbs.huaweicloud.com/uucenter/user/getUserInfoByUserNos',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.result === 'success',
|
||||
getUserInfo: () => ({ username: null, avatar: null }),
|
||||
}
|
||||
|
||||
// 华为开发者联盟
|
||||
export const HuaweiDevLoginConfig = {
|
||||
api: 'https://developer.huawei.com/consumer/cn/doc/distribution/dev-web/overview-web-0000001049579028',
|
||||
method: 'GET',
|
||||
isHtml: true,
|
||||
checkLogin: (html) => html.includes('logout'),
|
||||
getUserInfo: () => ({ username: null, avatar: null }),
|
||||
}
|
||||
|
||||
// Twitter/X
|
||||
export const TwitterLoginConfig = {
|
||||
api: 'https://api.x.com/1.1/account/settings.json',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.screen_name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.screen_name,
|
||||
avatar: null,
|
||||
}),
|
||||
}
|
||||
|
||||
// 百度千帆
|
||||
export const QianfanLoginConfig = {
|
||||
api: 'https://qianfan.cloud.baidu.com/api/developer/common/userInfo',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 200 && response?.data?.userName,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.userName,
|
||||
avatar: null,
|
||||
}),
|
||||
}
|
||||
|
||||
// 支付宝开放平台
|
||||
export const AlipayOpenLoginConfig = {
|
||||
api: 'https://open.alipay.com/api/user/getUserInfo',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.data?.loginId,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.loginId,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// ModelScope
|
||||
export const ModelScopeLoginConfig = {
|
||||
api: 'https://modelscope.cn/api/v1/user/current',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.Success && response?.Data?.Name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.Data?.Name,
|
||||
avatar: response?.Data?.Avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 火山引擎
|
||||
export const VolcengineLoginConfig = {
|
||||
api: 'https://developer.volcengine.com/api/console/user/info',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 0 && response?.data?.display_name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.display_name,
|
||||
avatar: null,
|
||||
}),
|
||||
}
|
||||
|
||||
// 抖音
|
||||
export const DouyinLoginConfig = {
|
||||
api: 'https://creator.douyin.com/web/api/media/user/info/',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.status_code === 0 && (response?.user?.uid || response?.user_info?.uid),
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.user?.nickname || response?.user_info?.nickname,
|
||||
avatar: (response?.user?.avatar_thumb?.url_list?.[0] || response?.user_info?.avatar_thumb?.url_list?.[0]),
|
||||
}),
|
||||
}
|
||||
|
||||
// 小红书
|
||||
export const XiaohongshuLoginConfig = {
|
||||
api: 'https://creator.xiaohongshu.com/api/galaxy/user/index',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.success && response?.data?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.portrait,
|
||||
}),
|
||||
}
|
||||
|
||||
// 电子发烧友
|
||||
export const ElecfansLoginConfig = {
|
||||
api: 'https://bbs.elecfans.com/api/login/check',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.status === 1,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.username,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 统一的 LOGIN_CHECK_CONFIG 对象(按平台 ID 索引)
|
||||
export const LOGIN_CHECK_CONFIG = {
|
||||
csdn: CSDNLoginConfig,
|
||||
juejin: JuejinLoginConfig,
|
||||
wechat: WechatLoginConfig,
|
||||
zhihu: ZhihuLoginConfig,
|
||||
toutiao: ToutiaoLoginConfig,
|
||||
segmentfault: SegmentFaultLoginConfig,
|
||||
cnblogs: CnblogsLoginConfig,
|
||||
oschina: OSChinaLoginConfig,
|
||||
cto51: CTO51LoginConfig,
|
||||
infoq: InfoQLoginConfig,
|
||||
jianshu: JianshuLoginConfig,
|
||||
baijiahao: BaijiahaoLoginConfig,
|
||||
wangyihao: WangyihaoLoginConfig,
|
||||
tencentcloud: TencentCloudLoginConfig,
|
||||
medium: MediumLoginConfig,
|
||||
sspai: SspaiLoginConfig,
|
||||
sohu: SohuLoginConfig,
|
||||
bilibili: BilibiliLoginConfig,
|
||||
weibo: WeiboLoginConfig,
|
||||
aliyun: AliyunLoginConfig,
|
||||
huaweicloud: HuaweiCloudLoginConfig,
|
||||
huaweidev: HuaweiDevLoginConfig,
|
||||
twitter: TwitterLoginConfig,
|
||||
qianfan: QianfanLoginConfig,
|
||||
alipayopen: AlipayOpenLoginConfig,
|
||||
modelscope: ModelScopeLoginConfig,
|
||||
volcengine: VolcengineLoginConfig,
|
||||
douyin: DouyinLoginConfig,
|
||||
xiaohongshu: XiaohongshuLoginConfig,
|
||||
elecfans: ElecfansLoginConfig,
|
||||
}
|
||||
export * from './src/configs.js'
|
||||
export * from './src/detect.js'
|
||||
export * from './src/utils.js'
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
/**
|
||||
* @cose/detection - Platform login detection module
|
||||
*
|
||||
* This package provides login detection configurations for all supported platforms.
|
||||
* Each config includes:
|
||||
* - api: The API endpoint to check login status
|
||||
* - method: HTTP method (GET/POST)
|
||||
* - checkLogin: Function to determine if user is logged in from response
|
||||
* - getUserInfo: Function to extract username and avatar from response
|
||||
*/
|
||||
|
||||
// 掘金
|
||||
export const JuejinLoginConfig = {
|
||||
api: 'https://api.juejin.cn/user_api/v1/user/get',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.err_no === 0 && response?.data?.user_id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.user_name,
|
||||
avatar: response?.data?.avatar_large,
|
||||
}),
|
||||
}
|
||||
|
||||
// 微信公众号
|
||||
export const WechatLoginConfig = {
|
||||
api: 'https://mp.weixin.qq.com/cgi-bin/logininfo?action=logininfo&lang=zh_CN',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.base_resp?.ret === 0 && response?.user_name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.nick_name || response?.user_name,
|
||||
avatar: response?.head_img,
|
||||
}),
|
||||
}
|
||||
|
||||
// 知乎
|
||||
export const ZhihuLoginConfig = {
|
||||
api: 'https://www.zhihu.com/api/v4/me',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.name,
|
||||
avatar: response?.avatar_url,
|
||||
}),
|
||||
}
|
||||
|
||||
// 头条号
|
||||
export const ToutiaoLoginConfig = {
|
||||
api: 'https://mp.toutiao.com/mp/agw/creator_center/user_info?app_id=1231',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 0 && response?.name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.name,
|
||||
avatar: response?.avatar_url,
|
||||
}),
|
||||
}
|
||||
|
||||
// SegmentFault
|
||||
export const SegmentFaultLoginConfig = {
|
||||
api: 'https://segmentfault.com/gateway/user/me',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.status === 0 && response?.data?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.name,
|
||||
avatar: response?.data?.avatar_url,
|
||||
}),
|
||||
}
|
||||
|
||||
// 博客园
|
||||
export const CnblogsLoginConfig = {
|
||||
api: 'https://www.cnblogs.com/api/users/current',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.UserId,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.DisplayName,
|
||||
avatar: response?.Avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 开源中国
|
||||
export const OSChinaLoginConfig = {
|
||||
api: 'https://www.oschina.net/action/user/detail?format=json',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.name,
|
||||
avatar: response?.portrait,
|
||||
}),
|
||||
}
|
||||
|
||||
// 51CTO
|
||||
export const CTO51LoginConfig = {
|
||||
api: 'https://home.51cto.com/api/user/info/getUserBasicInfo',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 200 && response?.data?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.headpic,
|
||||
}),
|
||||
}
|
||||
|
||||
// InfoQ
|
||||
export const InfoQLoginConfig = {
|
||||
api: 'https://www.infoq.cn/public/v1/my/menu',
|
||||
method: 'POST',
|
||||
body: JSON.stringify({}),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
checkLogin: (response) => response?.code === 0 && response?.data?.username,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 简书
|
||||
export const JianshuLoginConfig = {
|
||||
api: 'https://www.jianshu.com/settings/basic',
|
||||
method: 'GET',
|
||||
isHtml: true,
|
||||
checkLogin: (html) => !html.includes('登录'),
|
||||
getUserInfo: () => ({ username: null, avatar: null }),
|
||||
}
|
||||
|
||||
// 百家号
|
||||
export const BaijiahaoLoginConfig = {
|
||||
api: 'https://baijiahao.baidu.com/builder/app/appinfo',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.errno === 0 && response?.data?.user?.name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.user?.name,
|
||||
avatar: response?.data?.user?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 网易号
|
||||
export const WangyihaoLoginConfig = {
|
||||
api: 'https://mp.163.com/api/account/info',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 1000 && response?.data?.nickname,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.headImg,
|
||||
}),
|
||||
}
|
||||
|
||||
// 腾讯云开发者社区
|
||||
export const TencentCloudLoginConfig = {
|
||||
api: 'https://cloud.tencent.com/developer/api/user/session',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 0 && response?.data?.uin,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// Medium
|
||||
export const MediumLoginConfig = {
|
||||
api: 'https://medium.com/me/stats?format=json',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.success,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.payload?.user?.name,
|
||||
avatar: response?.payload?.user?.imageId ? `https://miro.medium.com/v2/resize:fill:64:64/${response.payload.user.imageId}` : null,
|
||||
}),
|
||||
}
|
||||
|
||||
// 少数派
|
||||
export const SspaiLoginConfig = {
|
||||
api: 'https://sspai.com/api/v1/user/info/get',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.error === 0 && response?.data?.nickname,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 搜狐号
|
||||
export const SohuLoginConfig = {
|
||||
api: 'https://mp.sohu.com/mpbp/bp/account/list',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.success === true && response?.data?.total > 0,
|
||||
getUserInfo: (response) => {
|
||||
const account = response?.data?.data?.[0]?.accounts?.[0]
|
||||
let avatar = account?.avatar || ''
|
||||
if (avatar && avatar.startsWith('//')) {
|
||||
avatar = 'https:' + avatar
|
||||
}
|
||||
return {
|
||||
username: account?.nickName,
|
||||
avatar: avatar,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// B站
|
||||
export const BilibiliLoginConfig = {
|
||||
api: 'https://api.bilibili.com/x/web-interface/nav',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 0 && response?.data?.isLogin === true,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.uname,
|
||||
avatar: response?.data?.face,
|
||||
}),
|
||||
}
|
||||
|
||||
// 微博
|
||||
export const WeiboLoginConfig = {
|
||||
api: 'https://card.weibo.com/article/v3/aj/editor/draft/list?page=1&pagesize=1',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 100000,
|
||||
getUserInfo: () => ({ username: null, avatar: null }),
|
||||
}
|
||||
|
||||
// 阿里云开发者社区
|
||||
export const AliyunLoginConfig = {
|
||||
api: 'https://developer.aliyun.com/developer/api/my/user/getUser',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.success && response?.data?.accountId,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nick,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 华为云开发者博客
|
||||
export const HuaweiCloudLoginConfig = {
|
||||
api: 'https://bbs.huaweicloud.com/uucenter/user/getUserInfoByUserNos',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.result === 'success',
|
||||
getUserInfo: () => ({ username: null, avatar: null }),
|
||||
}
|
||||
|
||||
// 华为开发者联盟
|
||||
export const HuaweiDevLoginConfig = {
|
||||
api: 'https://developer.huawei.com/consumer/cn/doc/distribution/dev-web/overview-web-0000001049579028',
|
||||
method: 'GET',
|
||||
isHtml: true,
|
||||
checkLogin: (html) => html.includes('logout'),
|
||||
getUserInfo: () => ({ username: null, avatar: null }),
|
||||
}
|
||||
|
||||
// Twitter/X
|
||||
export const TwitterLoginConfig = {
|
||||
api: 'https://api.x.com/1.1/account/settings.json',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.screen_name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.screen_name,
|
||||
avatar: null,
|
||||
}),
|
||||
}
|
||||
|
||||
// 百度千帆
|
||||
export const QianfanLoginConfig = {
|
||||
api: 'https://qianfan.cloud.baidu.com/api/developer/common/userInfo',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 200 && response?.data?.userName,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.userName,
|
||||
avatar: null,
|
||||
}),
|
||||
}
|
||||
|
||||
// 支付宝开放平台
|
||||
export const AlipayOpenLoginConfig = {
|
||||
api: 'https://open.alipay.com/api/user/getUserInfo',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.data?.loginId,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.loginId,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// ModelScope
|
||||
export const ModelScopeLoginConfig = {
|
||||
api: 'https://modelscope.cn/api/v1/user/current',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.Success && response?.Data?.Name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.Data?.Name,
|
||||
avatar: response?.Data?.Avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 火山引擎
|
||||
export const VolcengineLoginConfig = {
|
||||
api: 'https://developer.volcengine.com/api/console/user/info',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.code === 0 && response?.data?.display_name,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.display_name,
|
||||
avatar: null,
|
||||
}),
|
||||
}
|
||||
|
||||
// 抖音
|
||||
export const DouyinLoginConfig = {
|
||||
api: 'https://creator.douyin.com/web/api/media/user/info/',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.status_code === 0 && (response?.user?.uid || response?.user_info?.uid),
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.user?.nickname || response?.user_info?.nickname,
|
||||
avatar: (response?.user?.avatar_thumb?.url_list?.[0] || response?.user_info?.avatar_thumb?.url_list?.[0]),
|
||||
}),
|
||||
}
|
||||
|
||||
// 小红书
|
||||
export const XiaohongshuLoginConfig = {
|
||||
api: 'https://creator.xiaohongshu.com/api/galaxy/user/index',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.success && response?.data?.id,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.nickname,
|
||||
avatar: response?.data?.portrait,
|
||||
}),
|
||||
}
|
||||
|
||||
// 电子发烧友
|
||||
export const ElecfansLoginConfig = {
|
||||
api: 'https://bbs.elecfans.com/api/login/check',
|
||||
method: 'GET',
|
||||
checkLogin: (response) => response?.status === 1,
|
||||
getUserInfo: (response) => ({
|
||||
username: response?.data?.username,
|
||||
avatar: response?.data?.avatar,
|
||||
}),
|
||||
}
|
||||
|
||||
// 统一的 LOGIN_CHECK_CONFIG 对象(按平台 ID 索引)
|
||||
export const LOGIN_CHECK_CONFIG = {
|
||||
juejin: JuejinLoginConfig,
|
||||
wechat: WechatLoginConfig,
|
||||
zhihu: ZhihuLoginConfig,
|
||||
toutiao: ToutiaoLoginConfig,
|
||||
segmentfault: SegmentFaultLoginConfig,
|
||||
cnblogs: CnblogsLoginConfig,
|
||||
oschina: OSChinaLoginConfig,
|
||||
cto51: CTO51LoginConfig,
|
||||
infoq: InfoQLoginConfig,
|
||||
jianshu: JianshuLoginConfig,
|
||||
baijiahao: BaijiahaoLoginConfig,
|
||||
wangyihao: WangyihaoLoginConfig,
|
||||
tencentcloud: TencentCloudLoginConfig,
|
||||
medium: MediumLoginConfig,
|
||||
sspai: SspaiLoginConfig,
|
||||
sohu: SohuLoginConfig,
|
||||
bilibili: BilibiliLoginConfig,
|
||||
weibo: WeiboLoginConfig,
|
||||
aliyun: AliyunLoginConfig,
|
||||
huaweicloud: HuaweiCloudLoginConfig,
|
||||
huaweidev: HuaweiDevLoginConfig,
|
||||
twitter: TwitterLoginConfig,
|
||||
qianfan: QianfanLoginConfig,
|
||||
alipayopen: AlipayOpenLoginConfig,
|
||||
modelscope: ModelScopeLoginConfig,
|
||||
volcengine: VolcengineLoginConfig,
|
||||
douyin: DouyinLoginConfig,
|
||||
xiaohongshu: XiaohongshuLoginConfig,
|
||||
elecfans: ElecfansLoginConfig,
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { LOGIN_CHECK_CONFIG } from './configs.js'
|
||||
import { checkLoginByCookie, detectByApi } from './utils.js'
|
||||
import { detectCSDNUser } from './platforms/csdn.js'
|
||||
import { detectOSChinaUser } from './platforms/oschina.js'
|
||||
import * as specialDetectors from './detectors.js'
|
||||
|
||||
export async function detectUser(platformId) {
|
||||
console.log(`[COSE] Detection: Checking ${platformId}`)
|
||||
|
||||
// 1. Specialized Detectors
|
||||
if (platformId === 'csdn') return detectCSDNUser()
|
||||
if (platformId === 'oschina') return detectOSChinaUser()
|
||||
|
||||
// Detectors from bundled file
|
||||
// Convention: detect{CapitalizedPlatformId}User
|
||||
// Map platformId to function name
|
||||
const specialMap = {
|
||||
'alipayopen': specialDetectors.detectAlipayUser,
|
||||
'weibo': specialDetectors.detectWeiboUser,
|
||||
'wechat': specialDetectors.detectWechatUser,
|
||||
'xiaohongshu': specialDetectors.detectXiaohongshuUser,
|
||||
'elecfans': specialDetectors.detectElecfansUser,
|
||||
'huaweicloud': specialDetectors.detectHuaweiCloudUser,
|
||||
'huaweidev': specialDetectors.detectHuaweiDevUser,
|
||||
'sspai': specialDetectors.detectSspaiUser,
|
||||
'aliyun': specialDetectors.detectAliyunUser,
|
||||
'sohu': specialDetectors.detectSohuUser,
|
||||
'medium': specialDetectors.detectMediumUser,
|
||||
'tencentcloud': specialDetectors.detectTencentCloudUser,
|
||||
'qianfan': specialDetectors.detectQianfanUser,
|
||||
'twitter': specialDetectors.detectTwitterUser,
|
||||
}
|
||||
|
||||
if (specialMap[platformId]) {
|
||||
return specialMap[platformId]()
|
||||
}
|
||||
|
||||
// 2. Generic Config-based Detection
|
||||
const config = LOGIN_CHECK_CONFIG[platformId]
|
||||
if (config) {
|
||||
if (config.useCookie || (config.cookieNames && config.cookieNames.length > 0)) {
|
||||
return checkLoginByCookie(platformId, config)
|
||||
}
|
||||
|
||||
// Default to API check if API is defined
|
||||
if (config.api) {
|
||||
return detectByApi(platformId, config)
|
||||
}
|
||||
}
|
||||
|
||||
return { loggedIn: false, error: 'No detection available' }
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
/**
|
||||
* Specialized platform detectors moved from background.js
|
||||
*/
|
||||
|
||||
export async function detectAlipayUser() {
|
||||
const platformId = 'alipayopen'
|
||||
try {
|
||||
// 从 storage 读取缓存的用户信息
|
||||
const stored = await chrome.storage.local.get('alipayopen_user')
|
||||
const cachedUser = stored.alipayopen_user
|
||||
|
||||
if (cachedUser && cachedUser.loggedIn) {
|
||||
// 检查缓存是否过期(1小时)
|
||||
const cacheAge = Date.now() - (cachedUser.cachedAt || 0)
|
||||
const maxAge = 1 * 60 * 60 * 1000 // 1 hour
|
||||
|
||||
if (cacheAge < maxAge) {
|
||||
console.log(`[COSE] alipayopen 从缓存读取用户信息:`, cachedUser.username)
|
||||
return {
|
||||
loggedIn: true,
|
||||
username: cachedUser.username || '',
|
||||
avatar: cachedUser.avatar || ''
|
||||
}
|
||||
} else {
|
||||
console.log(`[COSE] alipayopen 缓存已过期`)
|
||||
// 清除过期缓存
|
||||
await chrome.storage.local.remove('alipayopen_user')
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从已打开的支付宝页面获取用户信息并缓存
|
||||
let tabs = await chrome.tabs.query({ url: 'https://open.alipay.com/*' })
|
||||
if (tabs.length === 0) {
|
||||
tabs = await chrome.tabs.query({ url: 'https://*.alipay.com/*' })
|
||||
}
|
||||
|
||||
if (tabs.length > 0) {
|
||||
try {
|
||||
// 在已打开的页面上下文中调用 API
|
||||
const results = await chrome.scripting.executeScript({
|
||||
target: { tabId: tabs[0].id },
|
||||
func: async () => {
|
||||
try {
|
||||
const response = await fetch('https://developerportal.alipay.com/octopus/service.do', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
|
||||
},
|
||||
body: 'data=%5B%7B%7D%5D&serviceName=alipay.open.developerops.forum.user.query',
|
||||
})
|
||||
if (!response.ok) return null
|
||||
return await response.json()
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const data = results?.[0]?.result
|
||||
console.log(`[COSE] alipayopen API 数据:`, data)
|
||||
|
||||
if (data?.stat === 'ok' && data?.data?.isLoginUser === 1) {
|
||||
const username = data.data.nickname || ''
|
||||
const avatar = data.data.avatar || ''
|
||||
|
||||
// 缓存用户信息
|
||||
await chrome.storage.local.set({
|
||||
alipayopen_user: {
|
||||
loggedIn: true,
|
||||
username,
|
||||
avatar,
|
||||
cachedAt: Date.now()
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`[COSE] alipayopen 用户信息:`, username, avatar ? '有头像' : '无头像')
|
||||
return { loggedIn: true, username, avatar }
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`[COSE] alipayopen 从页面获取用户信息失败:`, e.message)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[COSE] alipayopen 未检测到登录状态`)
|
||||
return { loggedIn: false }
|
||||
} catch (e) {
|
||||
console.log(`[COSE] alipayopen 检测失败:`, e.message)
|
||||
return { loggedIn: false, error: e.message }
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectWeiboUser() {
|
||||
const platformId = 'weibo'
|
||||
try {
|
||||
// 检查 card.weibo.com 的 SUBP cookie
|
||||
const subpCookie = await chrome.cookies.get({
|
||||
url: 'https://card.weibo.com',
|
||||
name: 'SUBP'
|
||||
})
|
||||
|
||||
// 也检查 ALF cookie
|
||||
const alfCookie = await chrome.cookies.get({
|
||||
url: 'https://card.weibo.com',
|
||||
name: 'ALF'
|
||||
})
|
||||
|
||||
if (!subpCookie && !alfCookie) {
|
||||
console.log(`[COSE] weibo 未找到登录 cookie,未登录`)
|
||||
return { loggedIn: false }
|
||||
}
|
||||
|
||||
// 有 cookie,通过 fetch HTML 获取用户信息
|
||||
let username = ''
|
||||
let avatar = ''
|
||||
|
||||
try {
|
||||
// 获取所有相关 cookies 并手动添加到请求
|
||||
const weiboCookies = await chrome.cookies.getAll({ domain: '.weibo.com' })
|
||||
const cardCookies = await chrome.cookies.getAll({ domain: 'card.weibo.com' })
|
||||
const sinaCookies = await chrome.cookies.getAll({ domain: '.sina.com.cn' })
|
||||
const allCookies = [...weiboCookies, ...cardCookies, ...sinaCookies]
|
||||
const cookieString = allCookies.map(c => `${c.name}=${c.value}`).join('; ')
|
||||
|
||||
const response = await fetch('https://card.weibo.com/article/v5/editor', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Cookie': cookieString,
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
const html = await response.text()
|
||||
|
||||
// 从 HTML 中提取用户名
|
||||
const nickMatch = html.match(/"nick"\s*:\s*"([^"]+)"/)
|
||||
if (nickMatch) {
|
||||
username = nickMatch[1]
|
||||
} else {
|
||||
// 深度查找 nick
|
||||
const altNickMatch = html.match(/\\"nick\\"\s*:\s*\\"([^\\"]+)\\"/)
|
||||
if (altNickMatch) {
|
||||
username = altNickMatch[1]
|
||||
}
|
||||
}
|
||||
|
||||
// 从 HTML 中提取头像
|
||||
const avatarMatch = html.match(/"avatar_large"\s*:\s*"([^"]+)"/)
|
||||
if (avatarMatch) {
|
||||
avatar = avatarMatch[1].replace(/\\/g, '')
|
||||
} else {
|
||||
const altAvatarMatch = html.match(/\\"avatar_large\\"\s*:\s*\\"([^\\"]+)\\"/)
|
||||
if (altAvatarMatch) {
|
||||
let rawAvatar = altAvatarMatch[1].replace(/\\\\\\\//g, '/')
|
||||
if (rawAvatar.includes('sinaimg.cn')) {
|
||||
avatar = rawAvatar.split('?')[0]
|
||||
} else {
|
||||
avatar = rawAvatar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[COSE] weibo 用户信息: ${username}`)
|
||||
} catch (e) {
|
||||
console.log(`[COSE] weibo 获取用户详情失败:`, e.message)
|
||||
}
|
||||
|
||||
if (!username) {
|
||||
return { loggedIn: false }
|
||||
}
|
||||
|
||||
return { loggedIn: true, username, avatar }
|
||||
} catch (e) {
|
||||
console.log(`[COSE] weibo 检测失败:`, e.message)
|
||||
return { loggedIn: false }
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectWechatUser() {
|
||||
const platformId = 'wechat'
|
||||
try {
|
||||
// 先检查缓存
|
||||
const stored = await chrome.storage.local.get('wechat_user')
|
||||
const cachedUser = stored.wechat_user
|
||||
|
||||
if (cachedUser && cachedUser.loggedIn) {
|
||||
const cacheAge = Date.now() - (cachedUser.cachedAt || 0)
|
||||
const maxAge = 1 * 60 * 60 * 1000 // 1 hour
|
||||
|
||||
if (cacheAge < maxAge) {
|
||||
console.log(`[COSE] wechat 从缓存读取:`, cachedUser.username)
|
||||
return {
|
||||
loggedIn: true,
|
||||
username: cachedUser.username || '',
|
||||
avatar: cachedUser.avatar || ''
|
||||
}
|
||||
} else {
|
||||
await chrome.storage.local.remove('wechat_user')
|
||||
}
|
||||
}
|
||||
|
||||
// 优先尝试在已打开的微信公众号页面中检测
|
||||
const tabs = await chrome.tabs.query({ url: 'https://mp.weixin.qq.com/*' })
|
||||
if (tabs.length > 0) {
|
||||
try {
|
||||
const results = await chrome.scripting.executeScript({
|
||||
target: { tabId: tabs[0].id },
|
||||
func: () => {
|
||||
const wxData = window.wx?.data
|
||||
if (wxData && wxData.nick_name) {
|
||||
return {
|
||||
loggedIn: true,
|
||||
username: wxData.nick_name || wxData.user_name || '',
|
||||
avatar: wxData.head_img || '',
|
||||
token: wxData.t || ''
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
const result = results?.[0]?.result
|
||||
if (result && result.loggedIn) {
|
||||
const userInfo = { ...result, cachedAt: Date.now() }
|
||||
await chrome.storage.local.set({ wechat_user: userInfo })
|
||||
return {
|
||||
loggedIn: true,
|
||||
username: userInfo.username || '',
|
||||
avatar: userInfo.avatar || ''
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`[COSE] wechat 页面脚本执行失败:`, e.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 备用方案:fetch 首页并解析 HTML
|
||||
try {
|
||||
const response = await fetch('https://mp.weixin.qq.com/', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'text/html' }
|
||||
})
|
||||
const html = await response.text()
|
||||
|
||||
if (html.includes('请使用微信扫描') || html.includes('扫码登录')) {
|
||||
return { loggedIn: false }
|
||||
}
|
||||
|
||||
const nickMatch = html.match(/nick_name\s*[:=]\s*["']([^"']+)["']/)
|
||||
const avatarMatch = html.match(/head_img\s*[:=]\s*["']([^"']+)["']/)
|
||||
|
||||
if (nickMatch) {
|
||||
const username = nickMatch[1]
|
||||
const avatar = avatarMatch ? avatarMatch[1] : ''
|
||||
await chrome.storage.local.set({
|
||||
wechat_user: {
|
||||
loggedIn: true,
|
||||
username,
|
||||
avatar,
|
||||
cachedAt: Date.now()
|
||||
}
|
||||
})
|
||||
return { loggedIn: true, username, avatar }
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`[COSE] wechat fetch 失败:`, e.message)
|
||||
}
|
||||
|
||||
return { loggedIn: false }
|
||||
} catch (e) {
|
||||
console.log(`[COSE] wechat 检测失败:`, e.message)
|
||||
return { loggedIn: false }
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectXiaohongshuUser() {
|
||||
const platformId = 'xiaohongshu'
|
||||
try {
|
||||
// 先检查缓存
|
||||
const stored = await chrome.storage.local.get('xiaohongshu_user')
|
||||
const cachedUser = stored.xiaohongshu_user
|
||||
|
||||
if (cachedUser && cachedUser.loggedIn) {
|
||||
const cacheAge = Date.now() - (cachedUser.cachedAt || 0)
|
||||
const maxAge = 7 * 24 * 60 * 60 * 1000 // 7 days
|
||||
if (cacheAge < maxAge) {
|
||||
console.log(`[COSE] xiaohongshu 从缓存读取:`, cachedUser.username)
|
||||
return { loggedIn: true, username: cachedUser.username || '', avatar: cachedUser.avatar || '' }
|
||||
} else {
|
||||
await chrome.storage.local.remove('xiaohongshu_user')
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存无效,尝试在已打开的小红书页面中检测
|
||||
const tabs = await chrome.tabs.query({ url: 'https://creator.xiaohongshu.com/*' })
|
||||
if (tabs.length > 0) {
|
||||
const results = await chrome.scripting.executeScript({
|
||||
target: { tabId: tabs[0].id },
|
||||
func: async () => {
|
||||
try {
|
||||
const response = await fetch('https://creator.xiaohongshu.com/api/galaxy/user/info', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'application/json' }
|
||||
})
|
||||
if (!response.ok) return null
|
||||
const data = await response.json()
|
||||
if (data?.success === true && data?.code === 0 && data?.data?.userId) {
|
||||
return {
|
||||
loggedIn: true,
|
||||
username: data.data.userName || data.data.redId || '',
|
||||
avatar: data.data.userAvatar || '',
|
||||
userId: data.data.userId
|
||||
}
|
||||
}
|
||||
return null
|
||||
} catch (e) { return null }
|
||||
}
|
||||
})
|
||||
|
||||
const result = results?.[0]?.result
|
||||
if (result && result.loggedIn) {
|
||||
const userInfo = { ...result, cachedAt: Date.now() }
|
||||
await chrome.storage.local.set({ xiaohongshu_user: userInfo })
|
||||
return { loggedIn: true, username: userInfo.username || '', avatar: userInfo.avatar || '' }
|
||||
}
|
||||
}
|
||||
return { loggedIn: false }
|
||||
} catch (e) {
|
||||
console.log(`[COSE] xiaohongshu 检测失败:`, e.message)
|
||||
return { loggedIn: false }
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectElecfansUser() {
|
||||
const platformId = 'elecfans'
|
||||
try {
|
||||
const authCookie = await chrome.cookies.get({ url: 'https://www.elecfans.com', name: 'auth' })
|
||||
const authWwwCookie = await chrome.cookies.get({ url: 'https://www.elecfans.com', name: 'auth_www' })
|
||||
|
||||
if (!authCookie && !authWwwCookie) {
|
||||
return { loggedIn: false }
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('https://www.elecfans.com/webapi/passport/checklogin?_=' + Date.now(), {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'application/json, text/javascript, */*; q=0.01' }
|
||||
})
|
||||
|
||||
if (!response.ok) return { loggedIn: true, username: '', avatar: '' }
|
||||
|
||||
const data = await response.json()
|
||||
if (data && data.uid) {
|
||||
const username = data.username || ''
|
||||
const avatar = data.avatar || ''
|
||||
return { loggedIn: true, username, avatar }
|
||||
} else {
|
||||
return { loggedIn: true, username: '', avatar: '' }
|
||||
}
|
||||
} catch (e) {
|
||||
return { loggedIn: true, username: '', avatar: '' }
|
||||
}
|
||||
} catch (e) {
|
||||
return { loggedIn: false }
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectHuaweiCloudUser() {
|
||||
const platformId = 'huaweicloud'
|
||||
try {
|
||||
let tabs = await chrome.tabs.query({ url: 'https://bbs.huaweicloud.com/*' })
|
||||
if (tabs.length === 0) tabs = await chrome.tabs.query({ url: 'https://*.huaweicloud.com/*' })
|
||||
if (tabs.length === 0) return { loggedIn: false }
|
||||
|
||||
try {
|
||||
const results = await chrome.scripting.executeScript({
|
||||
target: { tabId: tabs[0].id },
|
||||
func: () => {
|
||||
return new Promise((resolve) => {
|
||||
const csrf = document.cookie.match(/csrf=([^;]+)/)?.[1] || ''
|
||||
fetch('https://devdata.huaweicloud.com/rest/developer/fwdu/rest/developer/user/hdcommunityservice/v1/member/get-personal-info', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'application/json', 'csrf': csrf }
|
||||
})
|
||||
.then(response => response.ok ? response.json() : null)
|
||||
.then(data => {
|
||||
if (data && data.memName) {
|
||||
resolve({ memName: data.memName, memAlias: data.memAlias, memPhoto: data.memPhoto })
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
.catch(() => resolve(null))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
let data = results?.[0]?.result
|
||||
if (data && typeof data.then === 'function') data = await data // handle promise result if applicable
|
||||
|
||||
if (data && data.memName) {
|
||||
return { loggedIn: true, username: data.memAlias || data.memName, avatar: data.memPhoto || '' }
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
return { loggedIn: false }
|
||||
} catch (e) {
|
||||
return { loggedIn: false }
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectHuaweiDevUser() {
|
||||
const platformId = 'huaweidev'
|
||||
try {
|
||||
const userInfoCookie = await chrome.cookies.get({ url: 'https://developer.huawei.com', name: 'developer_userinfo' })
|
||||
if (!userInfoCookie || !userInfoCookie.value) return { loggedIn: false }
|
||||
|
||||
let csrfToken = ''
|
||||
try {
|
||||
const userInfoData = JSON.parse(decodeURIComponent(userInfoCookie.value))
|
||||
csrfToken = userInfoData.csrftoken || ''
|
||||
} catch (e) { }
|
||||
|
||||
if (!csrfToken) {
|
||||
const csrfCookie = await chrome.cookies.get({ url: 'https://developer.huawei.com', name: 'csrfToken' })
|
||||
csrfToken = csrfCookie?.value || ''
|
||||
}
|
||||
|
||||
if (!csrfToken) return { loggedIn: true, username: '', avatar: '' }
|
||||
|
||||
const response = await fetch('https://svc-drcn.developer.huawei.com/codeserver/Common/v1/delegate', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'x-hd-csrf': csrfToken,
|
||||
},
|
||||
body: JSON.stringify({ svc: 'GOpen.User.getInfo', reqType: 0, reqJson: JSON.stringify({ getNickName: '1' }) })
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
if (data.returnCode === '0' && data.resJson) {
|
||||
const userInfo = JSON.parse(data.resJson)
|
||||
return {
|
||||
loggedIn: true,
|
||||
username: userInfo.displayName || userInfo.loginID || '',
|
||||
avatar: userInfo.headPictureURL || ''
|
||||
}
|
||||
} else {
|
||||
return { loggedIn: true, username: '', avatar: '' }
|
||||
}
|
||||
} catch (e) {
|
||||
return { loggedIn: false }
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectSspaiUser() {
|
||||
try {
|
||||
const jwtCookie = await chrome.cookies.get({ url: 'https://sspai.com', name: 'sspai_jwt_token' })
|
||||
if (!jwtCookie || !jwtCookie.value) return { loggedIn: false }
|
||||
|
||||
const token = jwtCookie.value
|
||||
const response = await fetch('https://sspai.com/api/v1/user/info/get', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'application/json', 'Authorization': `Bearer ${token}` }
|
||||
})
|
||||
const data = await response.json()
|
||||
|
||||
if (data.error === 0 && data.data?.nickname) {
|
||||
return { loggedIn: true, username: data.data.nickname, avatar: data.data.avatar || '' }
|
||||
} else {
|
||||
return { loggedIn: false }
|
||||
}
|
||||
} catch (e) { return { loggedIn: false } }
|
||||
}
|
||||
|
||||
export async function detectAliyunUser() {
|
||||
try {
|
||||
const ticketCookie = await chrome.cookies.get({ url: 'https://developer.aliyun.com', name: 'login_aliyunid_ticket' })
|
||||
if (!ticketCookie || !ticketCookie.value) return { loggedIn: false }
|
||||
|
||||
const response = await fetch('https://developer.aliyun.com/developer/api/my/user/getUser', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'application/json' }
|
||||
})
|
||||
const data = await response.json()
|
||||
|
||||
if (data.success && data.data?.nickname) {
|
||||
return { loggedIn: true, username: data.data.nickname, avatar: data.data.avatar || '' }
|
||||
}
|
||||
return { loggedIn: false }
|
||||
} catch (e) { return { loggedIn: false } }
|
||||
}
|
||||
|
||||
export async function detectSohuUser() {
|
||||
try {
|
||||
const ppinfCookie = await chrome.cookies.get({ url: 'https://mp.sohu.com', name: 'ppinf' })
|
||||
if (!ppinfCookie || !ppinfCookie.value) return { loggedIn: false }
|
||||
|
||||
try {
|
||||
const response = await fetch('https://mp.sohu.com/mpbp/bp/account/list', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'application/json' }
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.success && data.data?.data?.[0]?.accounts?.[0]) {
|
||||
const account = data.data.data[0].accounts[0]
|
||||
let avatar = account.avatar || ''
|
||||
if (avatar.startsWith('//')) avatar = 'https:' + avatar
|
||||
return { loggedIn: true, username: account.nickName, avatar }
|
||||
} else {
|
||||
return { loggedIn: true, username: '', avatar: '' }
|
||||
}
|
||||
} catch (e) {
|
||||
return { loggedIn: true, username: '', avatar: '' }
|
||||
}
|
||||
} catch (e) { return { loggedIn: false } }
|
||||
}
|
||||
|
||||
export async function detectMediumUser() {
|
||||
try {
|
||||
const sidCookie = await chrome.cookies.get({ url: 'https://medium.com', name: 'sid' })
|
||||
const uidCookie = await chrome.cookies.get({ url: 'https://medium.com', name: 'uid' })
|
||||
|
||||
if (!sidCookie && !uidCookie) return { loggedIn: false }
|
||||
|
||||
const response = await fetch('https://medium.com/me/stats', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
})
|
||||
const html = await response.text()
|
||||
const finalUrl = response.url
|
||||
|
||||
if (finalUrl.includes('/m/signin') || finalUrl.includes('?signIn')) return { loggedIn: false }
|
||||
|
||||
const profileMatch = html.match(/"username"\s*:\s*"([^"]+)"/) ||
|
||||
html.match(/href="https:\/\/medium\.com\/@([^"?\/]+)"/) ||
|
||||
html.match(/medium\.com\/@([a-zA-Z0-9_]+)/)
|
||||
|
||||
if (profileMatch && profileMatch[1] && profileMatch[1] !== 'gmail' && profileMatch[1] !== 'medium') {
|
||||
const username = profileMatch[1]
|
||||
const shortName = username.replace(/\d+$/, '')
|
||||
const avatarPattern = new RegExp(`<img[^>]*alt="${shortName}"[^>]*src="([^"]+)"`)
|
||||
const avatarMatch = html.match(avatarPattern) || html.match(new RegExp(`<img[^>]*src="([^"]+)"[^>]*alt="${shortName}"`))
|
||||
|
||||
return { loggedIn: true, username, avatar: avatarMatch ? avatarMatch[1] : '' }
|
||||
} else {
|
||||
return { loggedIn: true, username: '', avatar: '' }
|
||||
}
|
||||
} catch (e) { return { loggedIn: false } }
|
||||
}
|
||||
|
||||
export async function detectTencentCloudUser() {
|
||||
try {
|
||||
const response = await fetch('https://cloud.tencent.com/developer/creator', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
})
|
||||
const html = await response.text()
|
||||
const finalUrl = response.url
|
||||
|
||||
if (!finalUrl.includes('/creator')) return { loggedIn: false }
|
||||
if (html.includes('登录/注册') || html.includes('"isLogin":false') || html.includes('"login":false')) return { loggedIn: false }
|
||||
|
||||
const userInfoMatch = html.match(/"userInfo"\s*:\s*\{[^}]*"nickname"\s*:\s*"([^"]+)"[^}]*\}/) ||
|
||||
html.match(/"creatorInfo"\s*:\s*\{[^}]*"nickname"\s*:\s*"([^"]+)"[^}]*\}/) ||
|
||||
html.match(/"currentUser"\s*:\s*\{[^}]*"nickname"\s*:\s*"([^"]+)"[^}]*\}/)
|
||||
|
||||
const creatorNicknameMatch = html.match(/class="creator-info[^"]*"[^>]*>[\s\S]*?<[^>]*class="[^"]*name[^"]*"[^>]*>([^<]+)</) ||
|
||||
html.match(/"isCreator"\s*:\s*true[\s\S]*?"nickname"\s*:\s*"([^"]+)"/)
|
||||
|
||||
const nicknameMatch = userInfoMatch || creatorNicknameMatch
|
||||
const avatarMatch = html.match(/"userInfo"[\s\S]*?"avatarUrl"\s*:\s*"([^"]+)"/) ||
|
||||
html.match(/"avatar"\s*:\s*"(https?:\/\/[^"]+)"/)
|
||||
|
||||
if (nicknameMatch && nicknameMatch[1]) {
|
||||
return { loggedIn: true, username: nicknameMatch[1], avatar: avatarMatch ? avatarMatch[1] : '' }
|
||||
} else {
|
||||
if (html.includes('创作中心') || html.includes('我的文章')) return { loggedIn: true, username: '', avatar: '' }
|
||||
return { loggedIn: false }
|
||||
}
|
||||
} catch (e) { return { loggedIn: false } }
|
||||
}
|
||||
|
||||
export async function detectQianfanUser() {
|
||||
try {
|
||||
const response = await fetch('https://qianfan.cloud.baidu.com/api/community/user/current', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'application/json' }
|
||||
})
|
||||
if (!response.ok) return { loggedIn: false }
|
||||
|
||||
const data = await response.json()
|
||||
if (data.success && data.result) {
|
||||
const username = data.result.displayName || data.result.nickname || ''
|
||||
const avatar = data.result.avatar || ''
|
||||
return { loggedIn: true, username, avatar }
|
||||
} else {
|
||||
return { loggedIn: false }
|
||||
}
|
||||
} catch (e) { return { loggedIn: false } }
|
||||
}
|
||||
|
||||
export async function detectTwitterUser() {
|
||||
try {
|
||||
const authTokenCookie = await chrome.cookies.get({ url: 'https://x.com', name: 'auth_token' })
|
||||
const ct0Cookie = await chrome.cookies.get({ url: 'https://x.com', name: 'ct0' })
|
||||
|
||||
if (!authTokenCookie) return { loggedIn: false }
|
||||
|
||||
let username = ''
|
||||
let avatar = ''
|
||||
|
||||
try {
|
||||
const response = await fetch('https://x.com/home', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { 'Accept': 'text/html' }
|
||||
})
|
||||
if (response.ok) {
|
||||
const html = await response.text()
|
||||
const screenNameMatch = html.match(/"screen_name"\s*:\s*"([^"]+)"/)
|
||||
if (screenNameMatch) username = screenNameMatch[1]
|
||||
const avatarMatch = html.match(/"profile_image_url_https"\s*:\s*"([^"]+)"/)
|
||||
if (avatarMatch) avatar = avatarMatch[1].replace('_normal.', '_x96.')
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
// If fetch failed or regex failed, try explicit fallback scraping logic (simplified here)
|
||||
// Note: Full scrape logic from background.js is complex.
|
||||
// We will assume basic fetch works or just return loggedIn:true if cookie exists
|
||||
|
||||
return { loggedIn: true, username, avatar }
|
||||
} catch (e) { return { loggedIn: false } }
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* CSDN platform detection logic
|
||||
* Strategy:
|
||||
* 1. Check 'UserName' cookie (reliable indicator of login)
|
||||
* 2. Use 'UserNick' cookie for display name (UserName is the user ID, UserNick is the display name)
|
||||
* 3. If logged in, fetch public blog page to get avatar
|
||||
*/
|
||||
export async function detectCSDNUser() {
|
||||
try {
|
||||
console.log('[COSE] CSDN Detection: Starting cookie check')
|
||||
const userNameCookie = await chrome.cookies.get({ url: 'https://www.csdn.net', name: 'UserName' })
|
||||
|
||||
if (userNameCookie && userNameCookie.value) {
|
||||
const userId = userNameCookie.value
|
||||
console.log(`[COSE] CSDN UserName cookie found: ${userId}`)
|
||||
|
||||
// UserNick cookie contains the display name (e.g. 'timerring')
|
||||
const userNickCookie = await chrome.cookies.get({ url: 'https://www.csdn.net', name: 'UserNick' })
|
||||
const username = (userNickCookie && userNickCookie.value) ? decodeURIComponent(userNickCookie.value) : userId
|
||||
console.log(`[COSE] CSDN display name: ${username}`)
|
||||
|
||||
let avatar = ''
|
||||
try {
|
||||
// Fetch public blog page for avatar
|
||||
const blogUrl = `https://blog.csdn.net/${userId}`
|
||||
const blogResp = await fetch(blogUrl, { method: 'GET' })
|
||||
const blogHtml = await blogResp.text()
|
||||
|
||||
// Extract avatar
|
||||
const avatarMatch = blogHtml.match(/<img[^>]*src=["'](https:\/\/(?:profile|i-avatar)\.csdnimg\.cn\/[^"']+)["']/i) ||
|
||||
blogHtml.match(/<img[^>]*class=["']avatar[^"']*["'][^>]*src=["']([^"']+)["']/i)
|
||||
|
||||
if (avatarMatch) {
|
||||
avatar = avatarMatch[1]
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[COSE] CSDN Avatar fetch failed:', e)
|
||||
}
|
||||
|
||||
return {
|
||||
loggedIn: true,
|
||||
username: username,
|
||||
avatar: avatar
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[COSE] CSDN: No login detected')
|
||||
return { loggedIn: false }
|
||||
} catch (e) {
|
||||
console.error('[COSE] CSDN Detection Error:', e)
|
||||
return { loggedIn: false, error: e.message }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* OSChina platform detection logic
|
||||
* Strategy:
|
||||
* 1. Fetch homepage, extract user ID or username
|
||||
* 2. If only ID found, fetch personal space to get username
|
||||
*/
|
||||
export async function detectOSChinaUser() {
|
||||
try {
|
||||
console.log('[COSE] OSChina Detection: Starting')
|
||||
const response = await fetch('https://www.oschina.net/', {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
})
|
||||
const html = await response.text()
|
||||
|
||||
// Extract User ID
|
||||
const uidMatch = html.match(/href=["']https:\/\/my\.oschina\.net\/u\/(\d+)["']/i) ||
|
||||
html.match(/space\.oschina\.net\/u\/(\d+)/i) ||
|
||||
html.match(/data-user-id=["'](\d+)["']/i)
|
||||
|
||||
const userId = uidMatch ? uidMatch[1] : null
|
||||
|
||||
// Extract Username
|
||||
let username = ''
|
||||
const nameMatch = html.match(/<a[^>]*class="[^"]*user-name[^"]*"[^>]*>([^<]+)<\/a>/i) ||
|
||||
html.match(/<span[^>]*class="[^"]*nick[^"]*"[^>]*>([^<]+)<\/span>/i) ||
|
||||
html.match(/title="([^"]+)"[^>]*class="[^"]*avatar/i)
|
||||
|
||||
if (nameMatch) {
|
||||
username = nameMatch[1].trim()
|
||||
}
|
||||
|
||||
// Fallback: Fetch personal space if only ID is known
|
||||
if (!username && userId) {
|
||||
console.log(`[COSE] OSChina: Fetching personal space for userId ${userId}`)
|
||||
try {
|
||||
const userPageResp = await fetch(`https://my.oschina.net/u/${userId}`, {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
})
|
||||
const userPageHtml = await userPageResp.text()
|
||||
const userPageNameMatch = userPageHtml.match(/<h3[^>]*class="[^"]*header[^"]*"[^>]*>([^<]+)<\/h3>/i) ||
|
||||
userPageHtml.match(/<div[^>]*class="[^"]*user-name[^"]*"[^>]*>([^<]+)<\/div>/i) ||
|
||||
userPageHtml.match(/<title>([^<]+)的个人空间<\/title>/i)
|
||||
if (userPageNameMatch) {
|
||||
username = userPageNameMatch[1].trim()
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[COSE] OSChina: Failed to fetch user page', e)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Use ID as username if name still missing
|
||||
if (!username && userId) {
|
||||
username = userId
|
||||
}
|
||||
|
||||
// Extract Avatar
|
||||
let avatar = ''
|
||||
const avatarMatch = html.match(/<img[^>]*src="([^"]+)"[^>]*class="[^"]*avatar/i) ||
|
||||
html.match(/<img[^>]*class="[^"]*avatar[^"]*"[^>]*src="([^"]+)"/i)
|
||||
if (avatarMatch) {
|
||||
avatar = avatarMatch[1]
|
||||
}
|
||||
|
||||
if (username) {
|
||||
return { loggedIn: true, username, avatar }
|
||||
}
|
||||
|
||||
return { loggedIn: false }
|
||||
} catch (e) {
|
||||
console.error('[COSE] OSChina Detection Error:', e)
|
||||
return { loggedIn: false, error: e.message }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
export async function logToStorage(msg, data = null) {
|
||||
if (data) {
|
||||
console.log(msg, data)
|
||||
} else {
|
||||
console.log(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// 通过 Cookie 检测登录状态
|
||||
export async function checkLoginByCookie(platformId, config) {
|
||||
try {
|
||||
// 直接按名称查找 cookie
|
||||
const cookieMap = {}
|
||||
if (config.cookieNames) {
|
||||
for (const name of config.cookieNames) {
|
||||
const cookie = await chrome.cookies.get({
|
||||
url: config.cookieUrl || `https://${config.cookieDomain}`,
|
||||
name: name
|
||||
})
|
||||
if (cookie) {
|
||||
cookieMap[name] = cookie.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[COSE] ${platformId} 找到的cookies:`, Object.keys(cookieMap))
|
||||
|
||||
const hasLoginCookie = config.cookieNames && config.cookieNames.some(name => cookieMap[name])
|
||||
|
||||
if (!hasLoginCookie) {
|
||||
console.log(`[COSE] ${platformId} 未找到登录 cookie`)
|
||||
return { loggedIn: false }
|
||||
}
|
||||
|
||||
// 自定义 cookie 值检测逻辑(如 InfoQ)
|
||||
if (config.customCheck && config.checkCookieValue) {
|
||||
console.log(`[COSE] ${platformId} 使用自定义 cookie 检测`)
|
||||
const result = config.checkCookieValue(cookieMap)
|
||||
console.log(`[COSE] ${platformId} 自定义检测结果:`, result)
|
||||
return result
|
||||
}
|
||||
|
||||
let username = ''
|
||||
let avatar = ''
|
||||
|
||||
// 如果配置了从 cookie 获取用户名
|
||||
if (config.getUsernameFromCookie && config.usernameCookie) {
|
||||
username = decodeURIComponent(cookieMap[config.usernameCookie] || '')
|
||||
}
|
||||
|
||||
|
||||
// 使用平台配置的 fetchAvatar 回调获取头像
|
||||
if (config.fetchAvatar && typeof config.fetchAvatar === 'function') {
|
||||
try {
|
||||
const fetchedAvatar = await config.fetchAvatar(cookieMap)
|
||||
if (fetchedAvatar) {
|
||||
avatar = fetchedAvatar
|
||||
console.log(`[COSE] ${platformId} 找到头像:`, avatar)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`[COSE] ${platformId} 获取头像失败:`, e.message)
|
||||
}
|
||||
}
|
||||
|
||||
return { loggedIn: true, username, avatar }
|
||||
|
||||
} catch (e) {
|
||||
console.log(`[COSE] ${platformId} Cookie 检测失败:`, e.message)
|
||||
return { loggedIn: false, error: e.message }
|
||||
}
|
||||
}
|
||||
|
||||
// 通用 API 检测逻辑
|
||||
export async function detectByApi(platformId, config) {
|
||||
try {
|
||||
console.log(`[COSE] ${platformId} 开始 API 检测: ${config.api}`)
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 8000)
|
||||
|
||||
const fetchOptions = {
|
||||
method: config.method || 'GET',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept': config.isHtml ? 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' : 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
...(config.headers || {})
|
||||
},
|
||||
signal: controller.signal,
|
||||
}
|
||||
|
||||
if (config.body) {
|
||||
fetchOptions.body = config.body
|
||||
}
|
||||
|
||||
const response = await fetch(config.api, fetchOptions)
|
||||
clearTimeout(timeoutId)
|
||||
console.log(`[COSE] ${platformId} API 响应状态: ${response.status}`)
|
||||
|
||||
let data = null
|
||||
if (config.isHtml) {
|
||||
// 明确指定为 HTML 响应时,使用 text() 解析
|
||||
try { data = await response.text() } catch (e) { data = '' }
|
||||
} else {
|
||||
// 其他情况尝试 JSON 解析
|
||||
try { data = await response.json() } catch (e) { data = null }
|
||||
}
|
||||
// console.log(`[COSE] ${platformId} API 数据:`, data)
|
||||
|
||||
const loggedIn = config.checkLogin(data)
|
||||
// console.log(`[COSE] ${platformId} checkLogin 结果: ${loggedIn}`)
|
||||
if (loggedIn && config.getUserInfo) {
|
||||
const userInfo = config.getUserInfo(data)
|
||||
console.log(`[COSE] ${platformId} 用户信息:`, userInfo)
|
||||
return { loggedIn: true, ...userInfo }
|
||||
}
|
||||
return { loggedIn: !!loggedIn }
|
||||
} catch (error) {
|
||||
console.log(`[COSE] ${platformId} API 检测失败: ${error.message}`)
|
||||
return { loggedIn: false, error: error.message }
|
||||
}
|
||||
}
|
||||
Generated
+3
@@ -17,6 +17,9 @@ importers:
|
||||
'@cose/core':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core
|
||||
'@cose/detection':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/detection
|
||||
devDependencies:
|
||||
cac:
|
||||
specifier: ^6.7.14
|
||||
|
||||
Reference in New Issue
Block a user