Browse Source

add dingding_work login

master
秋田弘 5 months ago
parent
commit
5e2e0aa82e
  1. 10
      frontend/components.d.ts
  2. 1
      frontend/index.html
  3. 101
      frontend/public/ref/clientid.js
  4. 167
      frontend/public/ref/config.js
  5. 111
      frontend/public/ref/ding.html
  6. 78
      frontend/src/system/dinglogin.ts
  7. 11
      frontend/src/system/index.ts

10
frontend/components.d.ts

@ -76,13 +76,19 @@ declare module 'vue' {
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElFooter: typeof import('element-plus/es')['ElFooter']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
@ -91,10 +97,14 @@ declare module 'vue' {
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElText: typeof import('element-plus/es')['ElText']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTransfer: typeof import('element-plus/es')['ElTransfer']
ElTree: typeof import('element-plus/es')['ElTree']
Error: typeof import('./src/components/taskbar/Error.vue')['default']
FileIcon: typeof import('./src/components/builtin/FileIcon.vue')['default']
FileIconImg: typeof import('./src/components/builtin/FileIconImg.vue')['default']

1
frontend/index.html

@ -20,6 +20,7 @@
<body>
<div id="app"></div>
<!-- 获取浏览器地址参数 -->
<script type="module" src="/src/main.ts"></script>
</body>
</html>

101
frontend/public/ref/clientid.js

@ -1,101 +0,0 @@
function getOSInfo() {
const userAgent = navigator.userAgent;
if (/windows phone/i.test(userAgent)) {
return 'Windows Phone';
}
if (/win/i.test(userAgent)) {
return 'Windows';
}
if (/mac/i.test(userAgent)) {
return 'Mac';
}
if (/x11/i.test(userAgent)) {
return 'UNIX';
}
if (/android/i.test(userAgent)) {
return 'Android';
}
if (/iphone|ipad|ipod/i.test(userAgent)) {
return 'iOS';
}
return 'Unknown';
}
function bin2hex(s) {
s = encodeURI(s); // 只会有0-127的ascii不转化
let m = s.match(/%[\dA-F]{2}/g), a = s.split(/%[\dA-F]{2}/), i, j, n, t;
m.push("");
for (i in a) {
if (a[i] === "") { a[i] = m[i]; continue; }
n = "";
for (j in a[i]) {
t = a[i][j].charCodeAt().toString(16).toUpperCase();
if (t.length === 1) t = "0" + t;
n += "%" + t;
}
a[i] = n + m[i];
}
return a.join("").split("%").join("");
}
function getBrowserFingerprint() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d'); // 移除类型声明
const txt = window.location.hostname;
ctx.textBaseline = 'top';
ctx.font = '14px \'Arial\'';
ctx.textBaseline = 'alphabetic';
ctx.fillStyle = '#f60';
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = '#069';
ctx.fillText(txt, 2, 15);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText(txt, 4, 17);
const b64 = canvas.toDataURL().replace("data:image/png;base64,", "");
const bin = window.atob(b64);
const hash = bin2hex(bin.slice(-16, -12));
const fingerprint = [
navigator.platform,
navigator.product,
navigator.productSub,
navigator.appName,
navigator.appVersion,
navigator.javaEnabled(),
navigator.userAgent,
screen.width,
screen.height,
new Date().getTimezoneOffset(),
hash,
screen.colorDepth,
navigator.language,
navigator.hardwareConcurrency,
getOSInfo(),
navigator.maxTouchPoints,
navigator.doNotTrack,
navigator.cookieEnabled
].join('|');
return fingerprint;
}
function djb2Hash(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) + hash) + char; /* hash * 33 + c */
}
return hash.toString(36); // Convert to base 36 for shorter string
}
function GetClientId() {
let uuid = localStorage.getItem("ClientID");
if (!uuid) {
const fingerprint = getBrowserFingerprint();
uuid = djb2Hash(fingerprint).slice(0, 36);
localStorage.setItem("ClientID", uuid);
}
return uuid;
}

167
frontend/public/ref/config.js

@ -1,167 +0,0 @@
const getSystemConfig = (ifset = false) => {
const configSetting = localStorage.getItem('GodoOS-config') || '{}';
let config = JSON.parse(configSetting);
config.version ??= '1.0.4';
config.isFirstRun ??= false;
config.lang ??= '';
config.apiUrl ??= `${window.location.protocol}//${window.location.hostname}:56780`;
config.userType ??= 'person';
config.editorType ??= 'local';
config.onlyoffice ??= { url: '', secret: '' };
config.file ??= { isPwd: 0, pwd: '' };
config.fileInputPwd ??= [];
config.userInfo ??= {
url: '',
username: '',
password: '',
id: 0,
nickname: '',
avatar: '',
email: '',
phone: '',
desc: '',
job_number: '',
work_place: '',
hired_date: '',
ding_id: '',
role_id: 0,
roleName: '',
dept_id: 0,
deptName: '',
token: '',
user_auths: '',
user_shares: '',
isPwd: false
};
config.isApp = window.go ? true : false;
config.systemInfo ??= {};
config.theme ??= 'light';
config.storeType ??= localStorage.getItem('GodoOS-storeType') || 'local';
config.storePath ??= '';
config.netPath ??= '';
config.netPort ??= '56780';
config.background ??= {
url: '/image/bg/bg6.jpg',
type: 'image',
color: 'rgba(30, 144, 255, 1)',
imageList: [
'/image/bg/bg1.jpg',
'/image/bg/bg2.jpg',
'/image/bg/bg3.jpg',
'/image/bg/bg4.jpg',
'/image/bg/bg5.jpg',
'/image/bg/bg6.jpg',
'/image/bg/bg7.jpg',
'/image/bg/bg8.jpg',
'/image/bg/bg9.jpg',
]
};
config.account ??= { ad: false, username: '', password: '' };
if (config.userType === 'member') {
config.account.ad = false;
}
config.storenet ??= { url: '', username: '', password: '', isCors: '' };
config.webdavClient ??= { url: '', username: '', password: '' };
config.dbInfo ??= { url: '', username: '', password: '', dbname: '' };
config.chatConf ??= {
checkTime: '15',
first: '192',
second: '168',
thirdStart: '1',
thirdEnd: '1',
fourthStart: '2',
fourthEnd: '254'
};
config.desktopList ??= [];
config.menuList ??= [];
config.token ??= generateRandomString(16);
config = parseAiConfig(config);
if (ifset) {
setSystemConfig(config);
}
return config;
};
const setSystemConfig = (config) => {
localStorage.setItem('GodoOS-config', JSON.stringify(config));
localStorage.setItem('GodoOS-storeType', config.storeType);
};
function generateRandomString(length) {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const parseAiConfig = (config) => {
if (!config.ollamaUrl) {
config.ollamaUrl = `${window.location.protocol}//${window.location.hostname}:11434`
}
if (!config.ollamaDir) {
config.ollamaDir = ''
}
if (!config.aiDir) {
config.aiDir = ''
}
if (!config.aiUrl) {
config.aiUrl = config.apiUrl
}
//openai
if (!config.openaiUrl) {
config.openaiUrl = 'https://api.openai.com/v1'
}
if (!config.openaiSecret) {
config.openaiSecret = ""
}
//gitee
if (!config.giteeSecret) {
config.giteeSecret = ""
}
//cloudflare
if (!config.cloudflareUserId) {
config.cloudflareUserId = ""
}
if (!config.cloudflareSecret) {
config.cloudflareSecret = ""
}
if (!config.deepseekSecret) {
config.deepseekSecret = ""
}
if (!config.bigmodelSecret) {
config.bigmodelSecret = ""
}
if (!config.volcesSecret) {
config.volcesSecret = ""
}
if (!config.alibabaSecret) {
config.alibabaSecret = ""
}
if (!config.groqSecret) {
config.groqSecret = ""
}
if (!config.mistralSecret) {
config.mistralSecret = ""
}
if (!config.anthropicSecret) {
config.anthropicSecret = ""
}
if (!config.llamafamilySecret) {
config.llamafamilySecret = ""
}
if (!config.siliconflowSecret) {
config.siliconflowSecret = ""
}
return config
};

111
frontend/public/ref/ding.html

@ -1,111 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>钉钉授权页面</title>
<meta
name="renderer"
content="webkit"
/>
<meta
http-equiv="X-UA-Compatible"
content="IE=edge,chrome=1"
/>
<meta
http-equiv="Cache-Control"
content="no-cache, no-store, must-revalidate"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
</head>
<script src="https://g.alicdn.com/dingding/dingtalk-jsapi/3.0.12/dingtalk.open.js"></script>
<script src="./clientid.js"></script>
<script src="./config.js"></script>
<body>
<div id="ding"></div>
<script>
// 页面加载完成后执行登录
window.onload = function () {
getCropId();
};
async function getCropId() {
try {
const res = await fetch(
"http://server001.godoos.com/user/ding/conf"
);
const data = await res.json();
if (data.success) {
await getCode(data.data.id);
} else {
ElMessage.error("获取钉钉配置失败");
}
} catch (e) {
throw e;
}
}
// 获取code登录
async function getCode(corpId) {
dd.runtime.permission.requestAuthCode({
corpId: corpId,
onSuccess: async function (result) {
await toLogin(result.code);
},
onFail: function (err) {
document.getElementById("ding").innerHTML =
JSON.stringify(err);
},
});
}
// 登录接口
async function toLogin(code) {
const data = {
login_type: "dingtalk_workbench",
client_id: GetClientId(),
param: {
code,
},
};
document.getElementById("ding").innerHTML =
JSON.stringify(data);
const res = await fetch(
"http://server001.godoos.com/user/login",
{
method: "POST",
body: JSON.stringify(data),
}
);
const jsondata = await res.json();
if (jsondata.success) {
document.getElementById("ding").innerHTML =
JSON.stringify(jsondata);
// 从本地存储中尝试获取配置信息,若不存在则使用默认空对象
const config = getSystemConfig();
jsondata.data.url = config.userInfo.url;
config.userInfo = jsondata.data;
document.getElementById("ding").innerHTML =
JSON.stringify(config);
setSystemConfig(config);
window.location.href = "http://osadmin.godoos.com/";
// setStorage("Authorization", resData.data.token);
// setStorage("ClientID", resData.data.client_id);
// 缓存用户数据
// const userData = {
// userDept: resData.data.dept,
// userInfo: resData.data.user,
// };
// setStorage("userData", userData);
ElMessage.success("登录成功");
// router.push("/knowledge/#/home");
} else {
ElMessage.error("登录失败");
}
}
</script>
</body>
</html>

78
frontend/src/system/dinglogin.ts

@ -0,0 +1,78 @@
import { GetClientId } from "@/util/clientid";
import dd from "dingtalk-jsapi";
import { getSystemConfig, setSystemConfig } from "./config";
const config = getSystemConfig();
// 函数用于动态加载外部JS文件
function loadScript(url: string): Promise<void> {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.onload = () => resolve()
script.onerror = () => reject(new Error(`Failed to load script ${url}`))
document.head.appendChild(script)
})
}
const currentUrl = window.location.origin
export async function getCropId(): Promise<boolean> {
try {
const res = await fetch(currentUrl + "/user/ding/conf");
const data = await res.json();
console.log(data)
if (data.success) {
console.log(data.data.id)
alert(data.data.host);
return await getCode(data.data.id);
}
return false;
} catch (e) {
console.error(e);
return false;
}
}
// 获取code登录
async function getCode(corpId: string): Promise<boolean> {
// 加载钉钉登录脚本
await loadScript("https://g.alicdn.com/dingding/dingtalk-jsapi/3.0.12/dingtalk.open.js")
return new Promise((resolve) => {
dd.runtime.permission.requestAuthCode({
corpId: corpId,
onSuccess: async function (result: { code: string }) {
resolve(await toLogin(result.code));
},
onFail: function (err: any) {
console.log(err)
resolve(false);
},
} as any);
});
}
// 登录接口
async function toLogin(this: any, code: string): Promise<boolean> {
const data = {
login_type: "dingtalk_workbench",
client_id: GetClientId(),
param: {
code,
},
};
const res = await fetch(currentUrl + "/user/login", {
method: "POST",
body: JSON.stringify(data),
});
const jsondata = await res.json();
if (jsondata.success) {
jsondata.data.url = currentUrl;
config.userInfo = jsondata.data;
config.userType = "member";
setSystemConfig(config);
return true;
}
return false;
}

11
frontend/src/system/index.ts

@ -26,6 +26,7 @@ import { pick } from '../util/modash';
import { clearSystemConfig, fetchGet, getFileUrl, getSystemConfig, getSystemKey, setSystemConfig, setSystemKey } from './config';
import { OsFileInterface } from './core/FIleInterface';
import { extname } from './core/Path';
import { getCropId } from './dinglogin.ts';
import { initBuiltinApp, initBuiltinFileOpener } from './initBuiltin';
import { defaultConfig } from './initConfig';
import { Tray, TrayOptions } from './menu/Tary';
@ -102,7 +103,17 @@ export class System {
initBuiltinFileOpener(this); // 注册内建文件打开器
await this.initSavedConfig(); // 初始化保存的配置
// 判断是否登录
const login_type = new URLSearchParams(window.location.search).get("login_type");
if (login_type === "dingding") {
let islogin = await getCropId();
alert(islogin);
if (!islogin) {
console.log("登录失败,无法继续操作");
return;
}
}
await this.isLogin();
initEventListener(); // 初始化事件侦听

Loading…
Cancel
Save