Ver Fonte

feat: org sync from authing

MetaPunkGames há 8 meses atrás
pai
commit
e5a6248c0b

+ 6 - 0
README.md

@@ -61,6 +61,12 @@ https://lljydpt8egql.u2-dev.hep.com.cn/
 - 用户名 hanlei
 - 密码 13581837652
 
+### AKSK
+AccessKey ID:
+6686bffb373d06911e24a969
+AccessKey Secret:
+4e978331675938d1bc81fb109e67d59a
+
 ### 单页面应用
 AppID 6682ab96b7bd5db59d6785a0
 

+ 54 - 5
package-lock.json

@@ -22,6 +22,7 @@
         "@ionic/angular": "^8.2.2",
         "@ngx-translate/core": "^15.0.0",
         "@types/parse": "^3.0.9",
+        "authing-node-sdk": "^3.1.0",
         "ng-zorro-antd": "^18.0.0",
         "parse": "^5.1.0",
         "rxjs": "~7.8.0",
@@ -5744,6 +5745,42 @@
       "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
       "dev": true
     },
+    "node_modules/authing-node-sdk": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/authing-node-sdk/-/authing-node-sdk-3.1.0.tgz",
+      "integrity": "sha512-lpL/kzrfXk/jBapZzTLur4HTZg6m3yDNYG6PHt3vSIs8ALvXpyEtOS4toA19nzlBBJBUN7eft7xhlTV7ZW+6zg==",
+      "dependencies": {
+        "axios": "^0.26.1",
+        "base64url": "^3.0.1",
+        "buffer": "^6.0.3",
+        "crypto-js": "^4.1.1",
+        "jose": "4",
+        "ws": "^8.12.0"
+      }
+    },
+    "node_modules/authing-node-sdk/node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
     "node_modules/autoprefixer": {
       "version": "10.4.19",
       "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.19.tgz",
@@ -5880,7 +5917,6 @@
       "version": "1.5.1",
       "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
       "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -5905,6 +5941,14 @@
         "node": "^4.5.0 || >= 5.9"
       }
     },
+    "node_modules/base64url": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/base64url/-/base64url-3.0.1.tgz",
+      "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/batch": {
       "version": "0.6.1",
       "resolved": "https://registry.npmmirror.com/batch/-/batch-0.6.1.tgz",
@@ -6864,8 +6908,7 @@
     "node_modules/crypto-js": {
       "version": "4.2.0",
       "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
-      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
-      "optional": true
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
     },
     "node_modules/css-loader": {
       "version": "7.1.1",
@@ -8437,7 +8480,6 @@
       "version": "1.2.1",
       "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
       "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -9079,6 +9121,14 @@
         "jiti": "bin/jiti.js"
       }
     },
+    "node_modules/jose": {
+      "version": "4.15.9",
+      "resolved": "https://registry.npmmirror.com/jose/-/jose-4.15.9.tgz",
+      "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
+      "funding": {
+        "url": "https://github.com/sponsors/panva"
+      }
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -14263,7 +14313,6 @@
       "version": "8.17.1",
       "resolved": "https://registry.npmmirror.com/ws/-/ws-8.17.1.tgz",
       "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
-      "dev": true,
       "engines": {
         "node": ">=10.0.0"
       },

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "@ionic/angular": "^8.2.2",
     "@ngx-translate/core": "^15.0.0",
     "@types/parse": "^3.0.9",
+    "authing-node-sdk": "^3.1.0",
     "ng-zorro-antd": "^18.0.0",
     "parse": "^5.1.0",
     "rxjs": "~7.8.0",

+ 142 - 0
server/cloud/authing/func-authing-org-sync.js

@@ -0,0 +1,142 @@
+const { ManagementClient } = require('authing-node-sdk')
+const managementClient  = new ManagementClient({
+    accessKeyId: '6686bffb373d06911e24a969',
+    accessKeySecret: "4e978331675938d1bc81fb109e67d59a",
+    host: 'https://textbook.u2-dev.hep.com.cn', // 应用的认证地址
+})
+
+
+// const pgp = require('pg-promise')();
+// const pgClient = () => {return pgp("postgresql://textbook:Edu2024textbask@oss-cn-beijing-internal.aliyuncs.com:5432/textbook");}
+
+import { pgClient } from '../../db/pg-instance'
+export function defineAuthingDepartSync(){
+// function defineAuthingLogin(){
+    Parse.Cloud.define("authingDepartSync", async (request) => {
+        let result = await syncOrganizationsFromAuthing(token)
+        return result
+    },{
+        fields : {
+            token:{
+                required:true
+            }
+        }
+    });
+}
+  
+
+/**
+ * 同步全部组织架构 From Authing
+ * @desc
+ * https://docs.authing.cn/v3/reference/sdk/node/management/管理组织机构/list-organizations.html
+ * @param {*} token 
+ * @returns 
+ */
+async function syncOrganizationsFromAuthing(){
+    // 通过用户的 id_token 初始化之后获取用户信息
+    console.log("syncOrganizationsFromAuthing 2")
+    
+    console.log("managementClient")
+    let orgList = []
+    try{
+
+        let result = await managementClient.listOrganizations({
+            page: 1,
+            limit: 50,
+            withPost: true,
+            withCustomData: true
+        });
+        if(result?.data?.list?.length){
+            orgList = orgList.concat(result?.data?.list)
+            let departList = await getAllChildDepartments(orgList[0])
+            console.log(departList)
+            console.log(departList?.length)
+            let insertRes = await InsertAllDepartment(departList)
+            console.log(insertRes)
+            return {code:200,count:departList?.length,message:`成功导入${departList?.length}条`}
+        }
+        
+    }catch(err){
+        console.error(err)
+    }
+    return
+    // console.log(user)
+
+}
+module.exports.syncOrganizationsFromAuthing = syncOrganizationsFromAuthing
+
+async function InsertAllDepartment(departList){
+    // {
+        //     departmentId: '66868df17adf199d1c77875d',
+    //     organizationCode: '1000000',
+    //     name: '河北人民出版社有限责任公司',
+    //     description: '',
+    //     code: '30000095',
+    //     hasChildren: false,
+    //     isVirtualNode: false,
+    //     i18n: { name: [Object] },
+    //     customData: {},
+    //     status: true
+    //     createdAt: '2024-07-04T11:56:33.813Z',
+    //     updatedAt: '2024-07-04T11:56:33.813Z',
+    //   },
+    let rowData = []
+    departList.forEach(depart=>{
+        rowData.push([depart?.departmentId,depart?.organizationCode,depart?.name,depart?.description,depart?.branch,depart?.parent?.departmentId,depart?.code,depart?.status,depart?.createdAt,depart?.updatedAt])
+    })
+    let flattenedParams = rowData.flat();
+
+    let syncDepartSQL = `
+    INSERT INTO "_Session" ("objectId", "organizationCode", "name", "description","branch","parent","code", "status","createdAt","updatedAt")
+    VALUES
+        ${rowData.map((_, i) => `($${i * 5 + 1}, $${i * 5 + 2}, $${i * 5 + 3}, $${i * 5 + 4}, $${i * 5 + 5}, $${i * 5 + 6}, $${i * 5 + 7}, $${i * 5 + 8}, $${i * 5 + 9}, $${i * 5 + 10})`).join(',\n')}    
+    ON CONFLICT ("objectId") DO UPDATE
+    SET 
+        "organizationCode" = EXCLUDED."organizationCode",
+        "name" = EXCLUDED."name",
+        "description" = EXCLUDED."description",
+        "branch" = EXCLUDED."branch",
+        "parent" = EXCLUDED."parent",
+        "code" = EXCLUDED."code",
+        "status" = EXCLUDED."status",
+        "createdAt" = EXCLUDED."createdAt",
+        "updatedAt" = EXCLUDED."updatedAt";
+    `
+    let result = await pgClient().any(syncDepartSQL,flattenedParams);
+    return result
+}
+
+async function getAllChildDepartments(depart){
+    if(!depart?.hasChildren) return []
+    let departmentList = []
+
+    let childDepartList = await getDepartmentOfOrg(depart?.organizationCode,depart?.departmentId)
+    departmentList = departmentList.concat(childDepartList)
+    // console.log("childDepartList",childDepartList)
+    let pList = childDepartList.map(item=>getAllChildDepartments(item))
+    let resList = await Promise.all(pList);
+    resList.forEach((list,index)=>{
+        list = list.map(item=>{item.parent = childDepartList[index];return item})
+        departmentList = departmentList.concat(list)
+    })
+    return departmentList
+}
+
+async function getDepartmentOfOrg(organizationCode,departmentId){
+    let result = await managementClient.listChildrenDepartments({
+        // 替换组织 Code 和部门 ID
+        organizationCode: organizationCode,
+        departmentId: departmentId,
+        departmentIdType: 'department_id',
+        withCustomData: true
+      });
+      if(result?.data?.list?.length) return result?.data?.list
+      return []
+}
+
+// const crypto = require('crypto');
+// function generateObjectId(inputString) {
+//     const hash = crypto.createHash('sha256').update(inputString).digest('hex');
+//     const objectId = hash;
+//     return objectId;
+// }

+ 2 - 1
server/cloud/authing/index.js

@@ -1 +1,2 @@
-export * from "./func-authing-session-sync"
+export * from "./func-authing-session-sync"
+export * from "./func-authing-org-sync"

+ 15 - 0
server/cloud/authing/test/test-authing-org-sync.js

@@ -0,0 +1,15 @@
+const { syncOrganizationsFromAuthing } = require("../func-authing-org-sync");
+
+function main(){
+    console.log("syncOrganizationsFromAuthing")
+
+    syncOrganizationsFromAuthing().then(data=>{
+        
+    })
+}
+main()
+
+/**
+ * Cloud Code test
+ curl -X POST -H "Content-Type: application/json" -H 'X-Parse-Application-Id: edu-textbook' http://8.140.98.43/parse/functions/authingDepartSync
+ */

+ 2 - 0
server/server.js

@@ -44,6 +44,7 @@ global.config["LOCAL"] = argv.local || process.env["LOCAL"] || appConfig["LOCAL"
 
 import  {textbookRouter} from "./api/textbook/routes";
 import { defineAuthingLogin } from "./cloud/authing"
+import { defineAuthingDepartSync } from "./cloud/authing"
 import { importData } from "./db/func/import-data"
 
 
@@ -176,6 +177,7 @@ async function initParseAndDatabase(){
       }, 500);
       // 导入CloudCode
       defineAuthingLogin()
+      defineAuthingDepartSync()
     });
  
     console.log("正在启动管理看板...")