mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-06 08:32:51 +08:00
🌱 验证码登录请求草创 #118
This commit is contained in:
@@ -81,6 +81,7 @@
|
||||
"clipboard": "^2.0.11",
|
||||
"color-convert": "^2.0.1",
|
||||
"echarts": "^5.5.1",
|
||||
"geetest": "^4.1.2",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-md5": "^0.8.3",
|
||||
"pinia": "^2.1.7",
|
||||
@@ -132,6 +133,7 @@
|
||||
"typescript": "^5.5.3",
|
||||
"typescript-eslint": "^7.16.1",
|
||||
"vite": "^5.3.4",
|
||||
"vite-plugin-node-polyfills": "^0.22.0",
|
||||
"vite-plugin-vue-devtools": "^7.3.6",
|
||||
"vite-plugin-vuetify": "^2.0.3",
|
||||
"vue-eslint-parser": "^9.4.3",
|
||||
|
||||
979
pnpm-lock.yaml
generated
979
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @file plugins/Mys/index.ts
|
||||
* @description Mys plugin index
|
||||
* @since Beta v0.4.5
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import MysApi from "./api/index.js";
|
||||
import { getCaptcha, doCaptchaLogin } from "./request/doCaptchaLogin.js";
|
||||
import { getLoginQr, getLoginStatus } from "./request/doGameLogin.js";
|
||||
import { getCollectionData, getCollectionPosts } from "./request/getCollectionData.js";
|
||||
import getForumList from "./request/getForumList.js";
|
||||
@@ -50,6 +51,8 @@ const Mys = {
|
||||
User: {
|
||||
getQr: getLoginQr,
|
||||
getData: getLoginStatus,
|
||||
getCaptcha,
|
||||
login: doCaptchaLogin,
|
||||
},
|
||||
Vote: {
|
||||
get: getVoteInfo,
|
||||
|
||||
112
src/plugins/Mys/request/doCaptchaLogin.ts
Normal file
112
src/plugins/Mys/request/doCaptchaLogin.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/doCaptchaLogin.ts
|
||||
* @description 通过短信验证码登录账号获取 stoken
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { publicEncrypt } from "node:crypto";
|
||||
|
||||
import TGHttp from "../../../utils/TGHttp.js";
|
||||
import { getDeviceInfo } from "../../../utils/toolFunc.js";
|
||||
import TGConstant from "../../../web/constant/TGConstant.js";
|
||||
|
||||
const PUB_KEY = `-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDvekdPMHN3AYhm/vktJT+YJr7cI5DcsNK
|
||||
qdsx5DZX0gDuWFuIjzdwButrIYPNmRJ1G8ybDIF7oDW2eEpm5sMbL9zs9ExXCdvqrn51qELb
|
||||
qj0XxtMTIpaCHFSI50PfPpTFV9Xt/hmyVwokoOXFlAEgCn+QCgGs52bFoYMtyi+xEQIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
const EncryptAreaCode = rsaEncrypt("+86");
|
||||
|
||||
/**
|
||||
* @description rsa 加密
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} data - 待加密数据
|
||||
* @returns {string} 加密后数据
|
||||
*/
|
||||
function rsaEncrypt(data: string): string {
|
||||
const buffer = Buffer.from(data);
|
||||
return publicEncrypt(PUB_KEY, buffer).toString("base64");
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取短信验证码
|
||||
* @since Beta v0.5.1
|
||||
* @todo retcode 为-3101时,表示需要进行验证,需要从resp.headers["x-rpc-aigis"]中获取相关数据
|
||||
* @param {string} phone - 手机号
|
||||
* @returns {Promise<TGApp.Plugins.Mys.CaptchaLogin.CaptchaData | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function getCaptcha(
|
||||
phone: string,
|
||||
): Promise<TGApp.Plugins.Mys.CaptchaLogin.CaptchaData | TGApp.BBS.Response.Base> {
|
||||
const url = "https://passport-api.mihoyo.com/account/ma-cn-verifier/verifier/createLoginCaptcha";
|
||||
const device_fp = getDeviceInfo("device_fp");
|
||||
const device_name = getDeviceInfo("device_name");
|
||||
const device_id = getDeviceInfo("device_id");
|
||||
const device_model = getDeviceInfo("product");
|
||||
const body = { area: EncryptAreaCode, phone: rsaEncrypt(phone) };
|
||||
const header = {
|
||||
"x-rpc-aigis": "",
|
||||
"x-rpc-app_version": TGConstant.BBS.VERSION,
|
||||
"x-rpc-client_type": "2",
|
||||
"x-rpc-app_id": TGConstant.BBS.APP_ID,
|
||||
"x-rpc-device_fp": device_fp,
|
||||
"x-rpc-device_name": device_name,
|
||||
"x-rpc-device_id": device_id,
|
||||
"x-rpc-device_model": device_model,
|
||||
};
|
||||
const resp = await TGHttp<
|
||||
TGApp.Plugins.Mys.CaptchaLogin.CaptchaResponse | TGApp.BBS.Response.Base
|
||||
>(url, {
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 通过短信验证码登录
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} phone - 手机号
|
||||
* @param {string} action_type - 操作类型
|
||||
* @param {string} captcha - 验证码
|
||||
* @returns {Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function doCaptchaLogin(
|
||||
phone: string,
|
||||
action_type: string,
|
||||
captcha: string,
|
||||
): Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | TGApp.BBS.Response.Base> {
|
||||
const url = "https://passport-api.mihoyo.com/account/ma-cn-passport/app/loginByMobileCaptcha";
|
||||
const device_fp = getDeviceInfo("device_fp");
|
||||
const device_name = getDeviceInfo("device_name");
|
||||
const device_id = getDeviceInfo("device_id");
|
||||
const device_model = getDeviceInfo("product");
|
||||
const body = {
|
||||
area: EncryptAreaCode,
|
||||
phone: rsaEncrypt(phone),
|
||||
action_type,
|
||||
captcha,
|
||||
};
|
||||
const header = {
|
||||
"x-rpc-aigis": "",
|
||||
"x-rpc-app_version": TGConstant.BBS.VERSION,
|
||||
"x-rpc-client_type": "2",
|
||||
"x-rpc-app_id": TGConstant.BBS.APP_ID,
|
||||
"x-rpc-device_fp": device_fp,
|
||||
"x-rpc-device_name": device_name,
|
||||
"x-rpc-device_id": device_id,
|
||||
"x-rpc-device_model": device_model,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.CaptchaLogin.LoginResponse | TGApp.BBS.Response.Base>(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
163
src/plugins/Mys/types/CaptchaLogin.d.ts
vendored
Normal file
163
src/plugins/Mys/types/CaptchaLogin.d.ts
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/CaptchaLogin.d.ts
|
||||
* @description Mys 插件 Captcha 登录类型定义文件
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Mys 插件 Captcha 登录类型
|
||||
* @since Beta v0.5.1
|
||||
* @namespace TGApp.Plugins.Mys.CaptchaLogin
|
||||
* @memberof TGApp.Plugins.Mys
|
||||
*/
|
||||
declare namespace TGApp.Plugins.Mys.CaptchaLogin {
|
||||
/**
|
||||
* @description 获取短信验证码返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface CaptchaResponse
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {CaptchaData} data 数据
|
||||
* @return GetCaptchaResponse
|
||||
*/
|
||||
interface CaptchaResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: CaptchaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取短信验证码返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface CaptchaData
|
||||
* @property {string} sent_new 是否发送新验证码
|
||||
* @property {string} countdown 倒计时
|
||||
* @property {string} action_type 操作类型
|
||||
* @return CaptchaData
|
||||
*/
|
||||
interface CaptchaData {
|
||||
sent_new: string;
|
||||
countdown: string;
|
||||
action_type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 短信验证码登录返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface LoginResponse
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {LoginData} data 数据
|
||||
* @return LoginResponse
|
||||
*/
|
||||
interface LoginResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: LoginData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 短信验证码登录返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface LoginData
|
||||
* @property {Token} token token数据
|
||||
* @property {UserInfo} user_info 用户信息
|
||||
* @property {ReactivateInfo} reactivate_info 重新激活信息
|
||||
* @property {string} login_ticket 登录 ticket
|
||||
* @property {boolean} new_user 是否新用户
|
||||
* @property {RealnameInfo} realname_info 实名信息
|
||||
* @property {boolean} need_realperson 是否需要实名认证
|
||||
* @property {string} oauth_hw_open_id 华为 open id
|
||||
* @return LoginData
|
||||
*/
|
||||
interface LoginData {
|
||||
token: Token;
|
||||
user_info: UserInfo;
|
||||
reactivate_info: ReactivateInfo;
|
||||
login_ticket: string;
|
||||
new_user: boolean;
|
||||
realname_info: RealnameInfo;
|
||||
need_realperson: boolean;
|
||||
oauth_hw_open_id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description token 数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface Token
|
||||
* @property {number} token_type token 类型
|
||||
* @property {string} token token
|
||||
* @return Token
|
||||
*/
|
||||
interface Token {
|
||||
token_type: number;
|
||||
token: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 用户信息
|
||||
* @since Beta v0.5.1
|
||||
* @interface UserInfo
|
||||
* @property {string} aid 账号 id
|
||||
* @property {string} mid mid
|
||||
* @property {string} account_name 账号名称
|
||||
* @property {string} email 邮箱
|
||||
* @property {number} is_email_verify 是否验证邮箱
|
||||
* @property {string} area_code 手机区号
|
||||
* @property {string} mobile 手机号
|
||||
* @property {string} safe_area_code 安全手机区号
|
||||
* @property {string} safe_mobile 安全手机号
|
||||
* @property {string} realname 真实姓名
|
||||
* @property {string} identity_code 身份证号
|
||||
* @property {string} rebind_area_code 重新绑定手机区号
|
||||
* @property {string} rebind_mobile 重新绑定手机号
|
||||
* @property {string} rebind_mobile_time 重新绑定手机时间
|
||||
* @property {string[]} links 账号绑定信息
|
||||
* @property {string} country 国家
|
||||
* @property {string} unmasked_email 邮箱
|
||||
* @property {number} unmasked_email_type 邮箱类型
|
||||
* @return UserInfo
|
||||
*/
|
||||
interface UserInfo {
|
||||
aid: string;
|
||||
mid: string;
|
||||
account_name: string;
|
||||
email: string;
|
||||
is_email_verify: number;
|
||||
area_code: string;
|
||||
mobile: string;
|
||||
safe_area_code: string;
|
||||
safe_mobile: string;
|
||||
realname: string;
|
||||
identity_code: string;
|
||||
rebind_area_code: string;
|
||||
rebind_mobile: string;
|
||||
rebind_mobile_time: string;
|
||||
links: string[];
|
||||
country: string;
|
||||
unmasked_email: string;
|
||||
unmasked_email_type: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 重新激活信息
|
||||
* @since Beta v0.5.1
|
||||
* @interface ReactivateInfo
|
||||
* @property {boolean} required 是否需要重新激活
|
||||
* @property {string} ticket 重新激活 ticket
|
||||
* @return ReactivateInfo
|
||||
*/
|
||||
interface ReactivateInfo {
|
||||
required: boolean;
|
||||
ticket: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 实名信息
|
||||
* @since Beta v0.5.1
|
||||
* @interface RealnameInfo
|
||||
* @property {boolean} required 是否需要实名认证
|
||||
* @property {string} action_type 操作类型
|
||||
* @property {string} action_ticket 操作 ticket
|
||||
* @return RealnameInfo
|
||||
*/
|
||||
interface RealnameInfo {
|
||||
required: boolean;
|
||||
action_type: string;
|
||||
action_ticket: string;
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ export function getInitDeviceInfo(): TGApp.App.Device.DeviceInfo {
|
||||
* @param {string} key - 设备信息 key
|
||||
* @returns {string} 设备信息
|
||||
*/
|
||||
export function getDeviceInfo(key: "device_id" | "device_fp"): string {
|
||||
export function getDeviceInfo(key: keyof TGApp.App.Device.DeviceInfo): string {
|
||||
const localDevice = localStorage.getItem("deviceInfo");
|
||||
let deviceInfo: TGApp.App.Device.DeviceInfo;
|
||||
if (localDevice === null) {
|
||||
|
||||
@@ -11,7 +11,13 @@
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"types": ["vite/client"],
|
||||
"types": [
|
||||
"vite/client",
|
||||
"src/types",
|
||||
"src/plugins/Bili/types",
|
||||
"src/plugins/Hutao/types",
|
||||
"src/plugins/Mys/types"
|
||||
],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"composite": true
|
||||
},
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* @file vite.config.ts
|
||||
* @description vite 配置文件
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import { defineConfig } from "vite";
|
||||
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
||||
import VueDevtools from "vite-plugin-vue-devtools";
|
||||
import vuetify from "vite-plugin-vuetify";
|
||||
|
||||
@@ -13,7 +14,7 @@ import buildTimePlugin from "./src/utils/TGBuild.js";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue(), vuetify(), buildTimePlugin(), VueDevtools()],
|
||||
plugins: [vue(), vuetify(), buildTimePlugin(), VueDevtools(), nodePolyfills()],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
// prevent vite from obscuring rust errors
|
||||
|
||||
Reference in New Issue
Block a user