deal ali cloud scan login
This commit is contained in:
parent
0998b6c3b0
commit
720fc60365
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,3 +9,4 @@ yarn-error.log*
|
||||
.pnp.js
|
||||
|
||||
.vscode/*
|
||||
.wrangler/*
|
||||
|
7
.idea/dictionaries/project.xml
generated
Normal file
7
.idea/dictionaries/project.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>alicloud</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
28
.idea/workspace.xml
generated
28
.idea/workspace.xml
generated
@ -4,7 +4,11 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="987ac7e2-ceaa-49a2-b5c9-aed65a7c598d" name="更改" comment="deal merge issue">
|
||||
<list default="true" id="987ac7e2-ceaa-49a2-b5c9-aed65a7c598d" name="更改" comment="deal ali cloud scan login">
|
||||
<change afterPath="$PROJECT_DIR$/.idea/dictionaries/project.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/aliui.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/oneui.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/public/index.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.ts" afterDir="false" />
|
||||
@ -14,6 +18,13 @@
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="TypeScript File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
@ -66,7 +77,7 @@
|
||||
<updated>1749696640494</updated>
|
||||
<workItem from="1749696641557" duration="119000" />
|
||||
<workItem from="1749696773818" duration="18000" />
|
||||
<workItem from="1749696801474" duration="15061000" />
|
||||
<workItem from="1749696801474" duration="20247000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="fin onedrive official token">
|
||||
<option name="closed" value="true" />
|
||||
@ -100,7 +111,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1749712420228</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="5" />
|
||||
<task id="LOCAL-00005" summary="deal merge issue">
|
||||
<option name="closed" value="true" />
|
||||
<created>1749714906877</created>
|
||||
<option name="number" value="00005" />
|
||||
<option name="presentableId" value="LOCAL-00005" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1749714906877</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="6" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@ -111,6 +130,7 @@
|
||||
<MESSAGE value="add readme" />
|
||||
<MESSAGE value="add onedrive area: cn us de" />
|
||||
<MESSAGE value="deal merge issue" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="deal merge issue" />
|
||||
<MESSAGE value="deal ali cloud scan login" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="deal ali cloud scan login" />
|
||||
</component>
|
||||
</project>
|
@ -1,4 +1,3 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="light">
|
||||
<head>
|
||||
@ -107,6 +106,7 @@
|
||||
<option value="onedrive_cn">OneDrive 世纪互联</option>
|
||||
<option value="onedrive_us">OneDrive 美国版本</option>
|
||||
<option value="onedrive_de">OneDrive 德国版本</option>
|
||||
<option value="alicloud_qr">阿里网盘 扫码登录</option>
|
||||
</select>
|
||||
|
||||
<div class="mb-3">
|
||||
@ -169,7 +169,8 @@
|
||||
});
|
||||
return;
|
||||
}
|
||||
let post_urls = "/onedrive/requests?client_uid=" + apps_uuid
|
||||
let apps_subs = apps_type.split("_")[0]
|
||||
let post_urls = "/" + apps_subs + "/requests?client_uid=" + apps_uuid
|
||||
+ "&client_key=" + apps_keys + "&apps_type=" + apps_type;
|
||||
try {
|
||||
const response = await fetch(post_urls, {
|
||||
@ -178,7 +179,33 @@
|
||||
// 解析响应内容 ===============================================
|
||||
const response_data = await response.json();
|
||||
if (response.status === 200) {
|
||||
window.location.href = response_data.text;
|
||||
if (apps_subs === "onedrive") {
|
||||
window.location.href = response_data.text;
|
||||
}
|
||||
if (apps_type === "alicloud_qr") {
|
||||
let sid = response_data.sid;
|
||||
await Swal.fire({
|
||||
position: 'top',
|
||||
icon: 'info',
|
||||
title: '扫码登录',
|
||||
html: `<div>请扫码登录,完成后点确定</div>` +
|
||||
`<img src="${response_data.text}" alt="">`,
|
||||
showConfirmButton: true
|
||||
});
|
||||
post_urls = "/alicloud/callback" +
|
||||
"?client_id=" + apps_uuid +
|
||||
"&client_secret=" + apps_keys +
|
||||
"&grant_type=" + "authorization_code" +
|
||||
"&code=" + sid
|
||||
let auth_post = await fetch(post_urls, {method: 'GET'});
|
||||
let auth_data = await auth_post.json();
|
||||
if (auth_post.status === 200) {
|
||||
window.location.href = `/?access_token=${auth_data.access_token}`
|
||||
+ `&refresh_token=${auth_data.refresh_token}`
|
||||
+ `&client_uid=${apps_uuid}`
|
||||
+ `&client_key=${apps_keys}`;
|
||||
}
|
||||
}
|
||||
} else Swal.fire({
|
||||
icon: 'error',
|
||||
title: "获取秘钥失败: " + response_data.text,
|
||||
|
101
src/aliui.ts
Normal file
101
src/aliui.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import {Context} from "hono";
|
||||
|
||||
const driver_map = [
|
||||
'https://openapi.aliyundrive.com/oauth/authorize/qrcode',
|
||||
'https://openapi.aliyundrive.com/oauth/access_token',
|
||||
'https://openapi.aliyundrive.com/oauth/qrcode',
|
||||
]
|
||||
|
||||
interface AliAccessTokenReq {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
grant_type: string;
|
||||
code: string;
|
||||
refresh_token: string;
|
||||
}
|
||||
|
||||
interface AliAccessTokenErr {
|
||||
code: string;
|
||||
message: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface AliQrcodeReq {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
scopes: string[];
|
||||
}
|
||||
|
||||
// 登录申请 ##############################################################################
|
||||
export async function alyLogin(c: Context) {
|
||||
try {
|
||||
const req: AliQrcodeReq = {
|
||||
client_id: <string>c.req.query('client_uid'),
|
||||
client_secret: <string>c.req.query('client_key'),
|
||||
scopes: ['user:base', 'file:all:read', 'file:all:write']
|
||||
}
|
||||
const response = await fetch(driver_map[0], {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error: AliAccessTokenErr = await response.json();
|
||||
return c.json({text: `${error.code}: ${error.message}`}, 403);
|
||||
}
|
||||
const data: Record<string, any> = await response.json();
|
||||
console.log(data);
|
||||
return c.json({
|
||||
"text": data.qrCodeUrl,
|
||||
"sid": data.sid
|
||||
}, 200);
|
||||
} catch (error) {
|
||||
return c.json({text: error}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 令牌申请 ##############################################################################
|
||||
export async function alyToken(c: Context) {
|
||||
const req: AliAccessTokenReq = {
|
||||
client_id: <string>c.req.query('client_id'),
|
||||
client_secret: <string>c.req.query('client_secret'),
|
||||
grant_type: <string>c.req.query('grant_type'),
|
||||
code: <string>c.req.query('code'),
|
||||
refresh_token: <string>c.req.query('refresh_token')
|
||||
};
|
||||
if (req.grant_type !== 'authorization_code' && req.grant_type !== 'refresh_token')
|
||||
return c.json({text: 'Incorrect GrantType'}, 400);
|
||||
if (req.grant_type === 'authorization_code' && !req.code)
|
||||
return c.json({text: 'Code missed'}, 400);
|
||||
if (req.grant_type === 'refresh_token' && req.refresh_token.split('.').length !== 3)
|
||||
return c.json({text: 'Incorrect refresh_token or missed',}, 400);
|
||||
if (req.grant_type === 'authorization_code') {
|
||||
let code_urls: string = 'https://openapi.aliyundrive.com/oauth/qrcode/' + req.code + '/status'
|
||||
let auth_post: Response = await fetch(code_urls, {method: 'GET'});
|
||||
let code_data: Record<string, string> = await auth_post.json();
|
||||
if (!auth_post.ok || code_data.status !== "LoginSuccess") {
|
||||
return c.json({text: 'Login failed:' + code_data.status}, 401);
|
||||
}
|
||||
req.code = code_data.authCode;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(driver_map[1], {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error: AliAccessTokenErr = await response.json();
|
||||
return c.json({text: `${error.code}: ${error.message}`,}, 403);
|
||||
}
|
||||
const data: Record<string, any> = await response.json();
|
||||
console.log(data);
|
||||
return c.json(data);
|
||||
} catch (error) {
|
||||
return c.json({text: error}, 500
|
||||
);
|
||||
}
|
||||
}
|
128
src/index.ts
128
src/index.ts
@ -3,132 +3,32 @@ import {KVNamespace} from '@cloudflare/workers-types';
|
||||
import {serveStatic} from 'hono/cloudflare-workers' // @ts-ignore
|
||||
import manifest from '__STATIC_CONTENT_MANIFEST'
|
||||
import * as local from "hono/cookie";
|
||||
import * as oneui from './oneui';
|
||||
import * as aliui from './aliui';
|
||||
|
||||
export type Bindings = {
|
||||
MAIN_URLS: string
|
||||
}
|
||||
const app = new Hono<{ Bindings: Bindings }>()
|
||||
app.use("*", serveStatic({manifest: manifest, root: "./"}));
|
||||
const driver_map: Record<string, string[]> = {
|
||||
"onedrive_go": [
|
||||
'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
|
||||
'https://login.microsoftonline.com/common/oauth2/v2.0/token'
|
||||
],
|
||||
"onedrive_cn": [
|
||||
'https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize',
|
||||
'https://microsoftgraph.chinacloudapi.cn/common/oauth2/v2.0/token'
|
||||
],
|
||||
"onedrive_de": [
|
||||
'https://login.microsoftonline.de/common/oauth2/v2.0/authorize',
|
||||
'https://graph.microsoft.de/common/oauth2/v2.0/token'
|
||||
],
|
||||
"onedrive_us": [
|
||||
'https://login.microsoftonline.us/common/oauth2/v2.0/authorize',
|
||||
'https://graph.microsoft.us/common/oauth2/v2.0/token'
|
||||
],
|
||||
}
|
||||
|
||||
// 登录申请 ##############################################################################
|
||||
app.get('/onedrive/requests', async (c) => {
|
||||
const client_uid = <string>c.req.query('client_uid');
|
||||
const client_key = <string>c.req.query('client_key');
|
||||
const driver_txt = <string>c.req.query('apps_type');
|
||||
const scopes_all = 'offline_access Files.ReadWrite.All';
|
||||
const client_url: string = driver_map[driver_txt][0];
|
||||
// 请求参数 ==========================================================================
|
||||
const params_all: Record<string, any> = {
|
||||
client_id: client_uid,
|
||||
scope: scopes_all,
|
||||
response_type: 'code',
|
||||
redirect_uri: 'https://' + c.env.MAIN_URLS + '/onedrive/callback'
|
||||
};
|
||||
const urlWithParams = new URL(client_url);
|
||||
Object.keys(params_all).forEach(key => {
|
||||
urlWithParams.searchParams.append(key, params_all[key]);
|
||||
});
|
||||
// 执行请求 ===========================================================================
|
||||
try {
|
||||
const response = await fetch(urlWithParams.href, {
|
||||
method: 'GET',
|
||||
});
|
||||
local.setCookie(c, 'client_uid', client_uid);
|
||||
local.setCookie(c, 'client_key', client_key);
|
||||
local.setCookie(c, 'apps_types', driver_txt);
|
||||
return c.json({text: response.url}, 200);
|
||||
} catch (error) {
|
||||
return c.json({text: error}, 500);
|
||||
}
|
||||
return oneui.oneLogin(c);
|
||||
})
|
||||
// 令牌申请 ##############################################################################
|
||||
app.get('/onedrive/callback', async (c) => {
|
||||
let login_data, client_uid, client_key, driver_txt, client_url, params_all;
|
||||
try { // 请求参数 ====================================================================
|
||||
login_data = <string>c.req.query('code');
|
||||
client_uid = <string>local.getCookie(c, 'client_uid')
|
||||
client_key = <string>local.getCookie(c, 'client_key')
|
||||
driver_txt = <string>local.getCookie(c, 'apps_types')
|
||||
client_url = driver_map[driver_txt][1];
|
||||
params_all = {
|
||||
client_id: client_uid,
|
||||
client_secret: client_key,
|
||||
redirect_uri: 'https://' + c.env.MAIN_URLS + '/onedrive/callback',
|
||||
code: login_data,
|
||||
grant_type: 'authorization_code'
|
||||
};
|
||||
} catch (error) {
|
||||
return c.redirect(
|
||||
`/?message_err=${"授权失败,请检查: <br>" +
|
||||
"1、应用ID和应用机密是否正确<br>" +
|
||||
"2、登录账号是否具有应用权限<br>" +
|
||||
"3、回调地址是否包括上面地址<br>" +
|
||||
"4、登录可能过期,请重新登录<br>" +
|
||||
"错误信息: <br> " + error}`
|
||||
+ `&client_uid=NULL`
|
||||
+ `&client_key=`);
|
||||
}
|
||||
// console.log(login_data);
|
||||
|
||||
// 执行请求 ===========================================================================
|
||||
try {
|
||||
const paramsString = new URLSearchParams(params_all).toString();
|
||||
const response: Response = await fetch(client_url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: paramsString,
|
||||
});
|
||||
// console.log(response);
|
||||
local.deleteCookie(c, 'client_uid');
|
||||
local.deleteCookie(c, 'client_key');
|
||||
local.deleteCookie(c, 'apps_types');
|
||||
if (!response.ok)
|
||||
return c.redirect(
|
||||
`/?message_err=${"授权失败,请检查: <br>" +
|
||||
"1、应用ID和应用机密是否正确<br>" +
|
||||
"2、登录账号是否具有应用权限<br>" +
|
||||
"3、回调地址是否包括上面地址<br>" +
|
||||
"错误信息: <br>" + response.text()}`
|
||||
+ `&client_uid=${client_uid}`
|
||||
+ `&client_key=${client_key}`);
|
||||
const json: Record<string, any> = await response.json();
|
||||
if (json.token_type === 'Bearer') {
|
||||
return c.redirect(
|
||||
`/?access_token=${json.access_token}`
|
||||
+ `&refresh_token=${json.refresh_token}`
|
||||
+ `&client_uid=${client_uid}`
|
||||
+ `&client_key=${client_key}`);
|
||||
}
|
||||
} catch (error) {
|
||||
return c.redirect(
|
||||
`/?message_err=${"授权失败,请检查: <br>" +
|
||||
"1、应用ID和应用机密是否正确<br>" +
|
||||
"2、登录账号是否具有应用权限<br>" +
|
||||
"3、回调地址是否包括上面地址<br>" +
|
||||
"错误信息: <br>" + error}`
|
||||
+ `&client_uid=${client_uid}`
|
||||
+ `&client_key=${client_key}`);
|
||||
}
|
||||
return oneui.oneToken(c);
|
||||
})
|
||||
|
||||
// 登录申请 ##############################################################################
|
||||
app.get('/alicloud/requests', async (c: Context) => {
|
||||
return aliui.alyLogin(c);
|
||||
});
|
||||
|
||||
// 令牌申请 ##############################################################################
|
||||
app.get('/alicloud/callback', async (c: Context) => {
|
||||
return aliui.alyToken(c);
|
||||
});
|
||||
|
||||
export default app
|
125
src/oneui.ts
Normal file
125
src/oneui.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import * as local from "hono/cookie";
|
||||
import {Context} from "hono";
|
||||
|
||||
const driver_map: Record<string, string[]> = {
|
||||
"onedrive_go": [
|
||||
'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
|
||||
'https://login.microsoftonline.com/common/oauth2/v2.0/token'
|
||||
],
|
||||
"onedrive_cn": [
|
||||
'https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize',
|
||||
'https://microsoftgraph.chinacloudapi.cn/common/oauth2/v2.0/token'
|
||||
],
|
||||
"onedrive_de": [
|
||||
'https://login.microsoftonline.de/common/oauth2/v2.0/authorize',
|
||||
'https://graph.microsoft.de/common/oauth2/v2.0/token'
|
||||
],
|
||||
"onedrive_us": [
|
||||
'https://login.microsoftonline.us/common/oauth2/v2.0/authorize',
|
||||
'https://graph.microsoft.us/common/oauth2/v2.0/token'
|
||||
],
|
||||
}
|
||||
|
||||
// 登录申请 ##############################################################################
|
||||
export async function oneLogin(c: Context) {
|
||||
const client_uid = <string>c.req.query('client_uid');
|
||||
const client_key = <string>c.req.query('client_key');
|
||||
const driver_txt = <string>c.req.query('apps_type');
|
||||
const scopes_all = 'offline_access Files.ReadWrite.All';
|
||||
const client_url: string = driver_map[driver_txt][0];
|
||||
// 请求参数 ==========================================================================
|
||||
const params_all: Record<string, any> = {
|
||||
client_id: client_uid,
|
||||
scope: scopes_all,
|
||||
response_type: 'code',
|
||||
redirect_uri: 'https://' + c.env.MAIN_URLS + '/onedrive/callback'
|
||||
};
|
||||
const urlWithParams = new URL(client_url);
|
||||
Object.keys(params_all).forEach(key => {
|
||||
urlWithParams.searchParams.append(key, params_all[key]);
|
||||
});
|
||||
// 执行请求 ===========================================================================
|
||||
try {
|
||||
const response = await fetch(urlWithParams.href, {
|
||||
method: 'GET',
|
||||
});
|
||||
local.setCookie(c, 'client_uid', client_uid);
|
||||
local.setCookie(c, 'client_key', client_key);
|
||||
local.setCookie(c, 'apps_types', driver_txt);
|
||||
return c.json({text: response.url}, 200);
|
||||
} catch (error) {
|
||||
return c.json({text: error}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// 令牌申请 ##############################################################################
|
||||
export async function oneToken(c: Context) {
|
||||
let login_data, client_uid, client_key, driver_txt, client_url, params_all;
|
||||
try { // 请求参数 ====================================================================
|
||||
login_data = <string>c.req.query('code');
|
||||
client_uid = <string>local.getCookie(c, 'client_uid')
|
||||
client_key = <string>local.getCookie(c, 'client_key')
|
||||
driver_txt = <string>local.getCookie(c, 'apps_types')
|
||||
client_url = driver_map[driver_txt][1];
|
||||
params_all = {
|
||||
client_id: client_uid,
|
||||
client_secret: client_key,
|
||||
redirect_uri: 'https://' + c.env.MAIN_URLS + '/onedrive/callback',
|
||||
code: login_data,
|
||||
grant_type: 'authorization_code'
|
||||
};
|
||||
} catch (error) {
|
||||
return c.redirect(
|
||||
`/?message_err=${"授权失败,请检查: <br>" +
|
||||
"1、应用ID和应用机密是否正确<br>" +
|
||||
"2、登录账号是否具有应用权限<br>" +
|
||||
"3、回调地址是否包括上面地址<br>" +
|
||||
"4、登录可能过期,请重新登录<br>" +
|
||||
"错误信息: <br> " + error}`
|
||||
+ `&client_uid=NULL`
|
||||
+ `&client_key=`);
|
||||
}
|
||||
// console.log(login_data);
|
||||
|
||||
// 执行请求 ===========================================================================
|
||||
try {
|
||||
const paramsString = new URLSearchParams(params_all).toString();
|
||||
const response: Response = await fetch(client_url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: paramsString,
|
||||
});
|
||||
// console.log(response);
|
||||
local.deleteCookie(c, 'client_uid');
|
||||
local.deleteCookie(c, 'client_key');
|
||||
local.deleteCookie(c, 'apps_types');
|
||||
if (!response.ok)
|
||||
return c.redirect(
|
||||
`/?message_err=${"授权失败,请检查: <br>" +
|
||||
"1、应用ID和应用机密是否正确<br>" +
|
||||
"2、登录账号是否具有应用权限<br>" +
|
||||
"3、回调地址是否包括上面地址<br>" +
|
||||
"错误信息: <br>" + response.text()}`
|
||||
+ `&client_uid=${client_uid}`
|
||||
+ `&client_key=${client_key}`);
|
||||
const json: Record<string, any> = await response.json();
|
||||
if (json.token_type === 'Bearer') {
|
||||
return c.redirect(
|
||||
`/?access_token=${json.access_token}`
|
||||
+ `&refresh_token=${json.refresh_token}`
|
||||
+ `&client_uid=${client_uid}`
|
||||
+ `&client_key=${client_key}`);
|
||||
}
|
||||
} catch (error) {
|
||||
return c.redirect(
|
||||
`/?message_err=${"授权失败,请检查: <br>" +
|
||||
"1、应用ID和应用机密是否正确<br>" +
|
||||
"2、登录账号是否具有应用权限<br>" +
|
||||
"3、回调地址是否包括上面地址<br>" +
|
||||
"错误信息: <br>" + error}`
|
||||
+ `&client_uid=${client_uid}`
|
||||
+ `&client_key=${client_key}`);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user