From c6d775cd7c231310c8300b87974735b23e425aa8 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 4 Jul 2025 18:18:41 +0800 Subject: [PATCH] first commit --- README.md | 28 +++++++ json/build.sh | 3 + kyapikeyserver/server.py | 176 +++++++++++++++++++++++++++++++++++++++ models/kydownapikey.xlsx | Bin 0 -> 16874 bytes models/kydownapp.xlsx | Bin 0 -> 16602 bytes pyproject.toml | 4 + setup.cfg | 34 ++++++++ 7 files changed, 245 insertions(+) create mode 100644 README.md create mode 100755 json/build.sh create mode 100644 kyapikeyserver/server.py create mode 100644 models/kydownapikey.xlsx create mode 100644 models/kydownapp.xlsx create mode 100644 pyproject.toml create mode 100644 setup.cfg diff --git a/README.md b/README.md new file mode 100644 index 0000000..935ff4a --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# 第三方应用登录服务器端 + +本模块为应用软件提供其他第三方应用的客户使用apikey自动登录 + +## 依赖的软件包 +* [ahserver](https://git.kaiyuancloud.cn/yumoqing/ahserver) +* [sqlor](https://git.kaiyuancloud.cn/yumoqing/sqlor) +* [apppublic](https://git.kaiyuancloud.cn/yumoqing/apppublic) + +## 安装 + +pip install kyapikeyserver + +## 业务流程 + +* 申请系统下位的apikey + +客户注册后,添加一个下位系统,并将系统自动生成的apikey和secretkey复制到实际的下午系统中,登记 + +* 用户同步 + +下位系统:用上位系统生成的系统ak,sk将需要同步的用户(支持多个)按照用户同步的格式上传到本地系统 + +上位系统:系统收到请求后,添加系统本身的机构和用户,并为此用户生成apikey,与下位系统的userid一起返回给下位系统 + + + + diff --git a/json/build.sh b/json/build.sh new file mode 100755 index 0000000..6fd2291 --- /dev/null +++ b/json/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +xls2ui -m ../models -o ../wwwroot kyapikey *.json diff --git a/kyapikeyserver/server.py b/kyapikeyserver/server.py new file mode 100644 index 0000000..25b8ca3 --- /dev/null +++ b/kyapikeyserver/server.py @@ -0,0 +1,176 @@ +from traceback import format_exc +from appPublic.log import debug, exception, info +from appPublic.timeUtils import curDateString +from uniqueID import getID +from time import time +from ahserver.serverenv import ServerEnv +from ahserver.auth_api import get_session_userinfo +from sqlor.dbpools import DBPools +form rbac.check_perm improt create_org, create_user + +from appPublic.aes import aes_encrypt_ecb, aes_decrypt_ecb + +return_messages = { + -9: '用户同步:未知未知错误', + -4: '用户同步:添加用户apikey失败', + -3: '用户同步:添加用户失败', + -2: '用户同步:添加机构失败', + -1: '用户同步:用户已同步' +} + +def get_dbname(): + dbname = get_serverenv('get_module_dbname')('dapi') + return dbname + +async def get_secretkey(sor, appid): + recs = await sor.R('kydownapp', {'id':appid}) + if len(recs) < 1: + return None + secret_key = recs[0].secret_key + f = get_serverenv('password_decode') + return f(secret_key).encode('utf-8') + +async def get_apikey_user(sor, apikey): + f = get_serverenv('password_encode') + apikey = f(apikey) + users = await sor.R('kydownapikey', {'apikey': apikey}) + if len(users) < 1: + return None + apiuser = users[0] + if not apiuser.enabled: + e = Exception(f'user(id={apiuser.userid}) is not enabled') + exception(f'{e},{format_exc()}') + raise e + if apiuser.expires_at < curDateString(): + e = Exception(f"user(id={apiuser.userid})'s apikey is expired({apiuser.expires_at})') + exception(f'{e}, {format_exc()}') + raise e + users = await sor.R('users', {'id': apiuser.userid, 'orgid': apiuser.orgid}) + if len(users) < 1: + e = Exception(f'user(id={apiuser.userid}) not found in users') + exception(f'{e}, {format_exc()}') + raise e + return users[0] + +async def get_user_from_bear_data(self, bear_data): + if not bear_data[:5] == 'Bear ': + return None + bear_data = bear_data[5:] + appid, cyber = bear_data.split('-:-') + db = DBPools() + dbname = get_dbname() + async with db.sqlorContext(dbname) as sor: + secretkey = await get_secretkey(sor, appid) + txt = aes_decrypt_ecb(secretkey, cyber) + t, apikey = txt.split(':') + userinfo = await get_apikey_user(apikey) + return userinfo + + return None + +def return_error(code): + return { + 'status':'error', + 'errcode': code, + 'errmsg': return_messages.get(code, '未定义信息') + } + +def return_success(data): + return { + 'status':'success', + 'data':data + } + +async def get_orgid_by_dorgid(sor, dappid, dorgid): + d = { + 'dappid':dappid, + 'dorgid':dorgid + } + recs = await sor.R('kydownapikey', d) + if len(recs) < 1: + return None + return recs[0].orgid + +async def check_duserid_exists(sor, dappid, dorgid, duserid): + d = { + 'dappid': dappid, + 'duserid': duserid, + 'dorgid': dorgid + } + recs = await sor.R('kwdownapikey', d) + if len(recs): + return True + return False + +async def add_organzation(sor, dappid, org): + id = getID() + org['id'] = id + await create_org(sor, org) + return id + +async def add_user(sor, user): + id = getID() + user['id'] = id + await create_user(sor, user, roles=user['roles'] + return id + +async def add_apikey(sor, dappid, dorgid, duserid, orgid, userid): + apikey = getID() + d = { + 'id': getID, + 'dappid': dappid, + 'dorgid': dorgid, + 'duserid': duserid, + 'orgid': orgid, + 'userid': userid, + 'apikey': id, + 'enabled': '1', + 'created_at': curDateString(), + 'expires_at': '9999-12-31' + } + await sor.C('kydownapikey', d) + return apikey + +async def sync_user(request, params_kw, *args, **kw): + dappid = params_kw.dappid + db = DBPools() + dbname = get_dbname() + userinfo = await get_session_userinfo(request) + async with db.sqlorContext(dbname) as sor: + ret_users = [] + roles = [{ + 'orgtypeid': 'customer', + 'roles': [ 'customer', 'syncuser' ] + }] + for o in params_kw.organizations: + for u in o['users']: + dorgid = o['id'] + duserid = u['id'] + orgid = await get_orgid_by_dorgid(sor, dappid, dorgid) + if orgid is None: + if o.get('parentid') is None: + o['parentid'] = userinfo.userorgid + else: + nparentid = await get_orgid_by_dorgid(sor, dappid, o.get('parentid')) + o['parentid'] = nparentid + orgid = await add_organzation(sor, dappid, o) + if orgid is None: + return return_error(-2) + u['orgid'] = o['id'] + u['roles'] = roles + exists = check_duserid_exists(sor, dappid, dorgid, duserid) + if exists: + return return_error(-1) + userid = await add_user(sor, u) + if userid is None: + return return_error(-3) + apikey = await add_apikey(sor, dappid, orgid, userid, u) + if apikey is None: + return return_error(-4) + ret_users.append({ + 'id': u['id'], + 'apikey': apikey + }) + return return_success(ret_users) + return return_error(-9) + diff --git a/models/kydownapikey.xlsx b/models/kydownapikey.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f452696f7c2d0f475438e43890b69a646567ba91 GIT binary patch literal 16874 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 literal 0 HcmV?d00001 diff --git a/models/kydownapp.xlsx b/models/kydownapp.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d02a9f258f5344a30ab01d81ad74eb30f9d35b2e GIT binary patch literal 16602 zcmeHuWmFtn);8|$?gV$2;O_1T?(XjHu7ThZG<J;O@bKySsgTb7v-Zm^*8I|KDjA z)f83d>FRU#v(HuqX;3gUAP68RARr)OAhUAGk8!|2KvaNJR3Io2ZDCs*Cu18YT@`mb zW5*BlZq`;rd0-%vIY1zQ`~Ua)KRg2?sw;BcjA-o~=lChB85Mp~QTlEh~+iI)en#|Mwjpfb9hJd%rfB?Cpld`3qC+EbYiW@zW8l!fYjGRZ`c zZL10xV<;L@U;20i#QQW4H8I2V47u?d9%^GPWgSFwv&tYZ;3EOB0sFFwWV;Ig;S3L8S`NvruNub{F_l?`mOS z{wo(}Ebj*pr_*lNq{i7VrbJLidiXn^gzQJZ=bN842=hF$?p6Ni!y9DtoUYPij~nLP z8^{L8;Oi?Wkix$a8TDhkF$jRjYXF|Y0*I{ZU~J{cK>x?_e+2$N%-X+v_3{K+X;3Dl zkkf!y;*mCupKNU zrHLyN8H>}ri(3>rxMa&y%Pl19`sy>ZA2#$zVhBn=q4k`-cWODQq$EZjoB^7(bAQAM zUe{V6>WJ$5BVEjEm$8)@u~@@~VKDEOsrtR)nEE&e%Ghen!{1T{0o`a213(#1a3CNY zAShrrD~3N=<7(?*X<%z>`N!_}PX+-4wm`tW|F>VQNwO9_j7Y(^!K;F1y2CCg9CY!u z(7{&Pw?N$luSnX=8Isy-?cIgZpn@w{wqPuQtBP!dC&0AI z4Il|m#Rzz$n2pEkj!V8cp4~Wp^kBYpXq!gcf$o{8AwLMaz>R48+{E}o+%|%H`O?hv zu})1Ag7s0>OxR(2th^WxgZ1b5(uy@f3)L_pIBMt_ikq3OU%SJ~zRPp~-1q%0%a;s=?Oa-d!0p>j*JnB>q6= zGfw{b_8s`AX}nfMDx!#rrV9OGscZi4rE3P)hM4ciTvnfTw_sI{wd$I4>y+DXh&5loEdmvq?Em+xEV7i{2E(ew9>6~ z6=3l%amDuL)Z->Q) z$+xaW7=Qe#Gd~CUIRlQf3Df8Ch-Y&cj`C5E(=U{j$*a$1A1^`Q@QiK}tAm(x5%~!s z)i9I;Ofpn|g~i|eeSY3tbEU)HNA10_hTJ(xNPY1ea*GTP_WsJp0G4; z>e~pI&vUZ%wfXallRT@y;t-i|kZybUbY5qOrkuLmtAYbOyfkJjBs^GnuK zi3;%}Xf?`Nu|!bMIMJ-3vu6wdIsZ>4urx2yR0Eix4A8GwznS1@W^C-_$nfip^$!)B znJ66#phWO(@&jSqZLi*xj6j^C5Q;Oq>TEh+LrgOmjgP{zyG+^3EzVS&)S?Kf|5x~z zF|HqnPv>N!c0h*BYTyy#$S?v-AMEStCgxL}IYs@QRa8~Xz`;GhBMD4T*S5v2T^fMx z{3$54bNZXvdlqUS$m&T~0xoPVtjU~xEE*OXAk1cM6F?FvGxQ{zg-nG^PW1P_?ZBmE z9FtSJhF~GhxW6`RRJ2WywLnsQSv+2jGhg)R4S_xR=t&7fHvG9k3ilf_-z^n^GDc=p zM_5Nn@o2zf(Q;GJD@Atzlk~8Cp8u1cFkO6(ve~i4huznsS-s zBdY|#fEXf0ZE|VyU=-~_gB-K;tm@`ypY*I~A7+(s)FKoXSQYXyjUsNxo75xxb^&Q4q8?8&^uQByJUOD3_>DCW_ ztMJh?pMHpd0s&b=|5q_*`%}yZZ2(Uja+>^rkaD^XwkISaK&LvpT3R~1$cbvOL0&(q zr0RVeBMdE}gOp+@-+#`*a(+5#l#j0Ie>s(rS+o*_!q|;5HRAa2a@d~oqd{~+!d5! z^rHk*z{Z=3q&bMtmcDw4B?Ia8kxge&_9!B53PgF#Py^=R$_muQxBKuG4`sPxxbfx2 z1WPcn| zIYg=gy!_Y6kV-NoRvhA?h_A~kB*5lRapFQXD_*;K-Fc6U5>rlxjo*(R@)jPRdOP9^ z!+qMeebKzy&qq||Ha%CKb%u+4X_u{*9<`Ply$2808$XL&kJ$)cZ|@YVO%kZUl~4u4 zks%7uMh#o<0wZy|0(G__QkBk``#}Yk4=wVQ%^%9(j5T)^zJMAeT`efbI#{nTIm@%S zv}w9Q2r00*qsG-*HJDDzy*1+kfd}rh7393ytidwiRw6Cp;QXj5yw3+MJK^8o)*q=o1DrcH|I z4tIKth;=ia+ogU<_^%0KjS+AR0m_YTiULu?!D^Q%A*6usm z8T~mrqozW6=jF7gYOqGGL)<$|nY$;qEyz~`qb6(Zo+EtYZyZE(9dFmXK*Wh?JeGT7 zIS)k>&P9D7jYF59ezuZO`%5YKyL;_?#|)J5eOYoGa6uRSgau$ zBYj+~>g?-kS9O*I7ps;L3)T}~4`a{4xe+Uu-{lZH`1W)Vidg}jYAxN~VeIZ;01Baq zGK@7YpzI|K9Gx%aEF|m2%gk%sNX8V* z8HJL}reY-_pP`#uLiNj}B=4-YR|QbVkRY|desol+9ztfs->yg5wRrQy&((!3P*!wn zLl*Xae==UypkT+@-jFl@O6<#{}LG^c6i%U72`^Nsu5a?pv5S`sJg|%ci%&mdc-a& zg0lqXA$(WYO={6&7JWhw*au0Z4Z*;uWI?bDP+tGJ_}=Ow-&+-e zcq^liv5+s0>OE_Ci%^L2YOhMhCFeEkAEV-ur;pSAf2xi&9KkkQvBF zMYzIC%VctU;`u4_jVHK22!#K5)Eiy@lA0ZymM$0Of@Ct?M=NXc#4jZyTr9#vdH;dF z_G4HBrb*J*;gca(Uan$H+z%Z?OfWEnB8s3-mXm*6Xffu>+tA<k(LfS5R;K99b4K<3art$NDoKtUd2ysDTD9|=H*oN3Vw!q&l?>@alc#E=$F`qUb$`2jX`wh0JZ{NQm1>k&4>3awmyvJ#Smg9k zt!@!@e!kz&Wf~e`!Lh6mI!br(8Kjb;GZDGZ-DpwKebPO1p#>hSo6jEgkbSG)jA8p0 zPXtyZf{f4sM9jk|rR@i@^*3~Xc)hm4nhE!Fb`g9p zHUM>g(ipPJS3(AcBejn)<_vZ<>_;Wrh!;qphPh4Tm)h!;)f2WI4bY@lr@fPUk3%+C+dW;~8uzl?c8rgK4$s%*PkIyJ3|emR zL+9|J@>-TF--73J@YZy?8%wp@Jche16=Wp2HHFl1N3Tx|>cNdh_F_<1ZDt@V>YFc6 zz!w#aQZo!jl$xpMQJf;rfq~HCNFCz@sqtm;8k9|`P@~E^K4iT}~eCuQqwX5q1%U#jDlO{V}sEWW5)Me9)YzR;pNSDDKVYBWf@yE?WkFSfI%d2}hR$1+X(mlcWvsp3jEX^?qXq4?Id>MCQXYK7 zw`M3^#Gn`(t#u42SKoLEL|YM_zM>BKP_2`<%5=pOa`So%*Ji z9!>Wr0#qr)t*D>xbLH1TU=%q01XpnOUcQe_EB z`ZOppqAK1!oaR@lh1SRu-ragwKC2R%o){`ngIC2KVZVoE6l2pcBv-0X zRwp3oHg&;?s*n0Gs(&xUf;EXx`pc`ScpY%WXY_ea!f6>2#%sLt9fvvh4@f%5{aP3u3b&5t}Z+b9*m_gL%h zrg|DHSrz)?b{`Fu&zv&sAfmgKI9dW;-8|B~wniWqlcw}Q!F?gdPv=h8D&+QV+99B# zprRSv@P$6XZm7bmJy11FOvnU}4G9Q0qP9B|4cjELkw_;5e^ugWrPw;bfKW?TJA z5<>L|10@B$Keb$W@Hd&IsI^y3hUciE&?tlk>pMA5-OqJ#`9^l%}QA#5Rad zOoNv`mC#A^tYdJPBbPl|m06M!^V=%pZETf#VG(YOA`_%)*XguZ?YcqR!NqQINTTno zsV~y8EBU4>a+Gert^r5}rlrgh-A_InUoumasdGJnzAOEN3{tDki3`t&wbzs6S}&H! zA%i5J6~Z5Gd(6*ehT8l3fjg=6!m{!anN+z6kf`+i+h&g;#p5}y&N<1Klz9_K@fId* zZ4%sEG~*vU5B#y)B%IHHk8>vO=Z+r%!P5*ZBe*6~0?b>r zVCLG9N~(uH>-<=oj7L5ZH;N_V=6b{h8ictez3)L;J0LG!fA>qG-t)5p*G4YXz`%7b zDoF}=LLbd!V_ULlO~RKTh_n4k;IN;@%ww!kx4t=`NTX%Sm#oUr=v9s#{imn&zip(E zL|NN>K)B#FV*Vf*>jEA|LO|=wl-iQ=DSdUI>ZZQ+w1VpMX_i3EQe}gMoT_))Ib42q z_WVduMHf_6scwn{cW4uYBhoq21baJ)h z!^>4+9wW>oUccLk%zUl0tgi!5%hEu;2w$uAp5=ET<+;n09+Xn%hrOybz|Tu51Mhpd z5iYE&;Tr6*$A@?sOqM^XOFf9;re00*7S`Wdcc+zEOxkgns~C6rUnRoeKq!?;G0tnX z2A2h&MVm}BMrn@VRKkt2FOV(TkDoTH?Zq#|!*B=cxou1fN;nR3C zMIi&-u+JKc^i9%8txGQbIkxP{A|prBW+6^2G@-T3xuYUZ6FK;>AnfP00_RTu&)bJ* z`RS^H7_pP#5^=0_Z<+VmBu-5aI}>+11J2k^S6pmD9`qa#@>fW6Xf%+zZeQZ8m76VH zy9VvB6F7^T1&%eZ=hfTQPmN!`XikTX3M>nP6({$)>4XcGBja!AXt_UZZ7dX^I8t_U zGeU)!8zXQJsqEZTByCP$bwp;>B^1v{m6qmj=R#-N8wifb*YvUjZ}T5#9%!(GeM=0D z%LwtHAm}K|0MaKfiYF<0^4+{m(m>8v7v;nkc}I~XGbEUfHmaxBog~FVHT#O+OPLpf zRA*%b;z=Jh3lt172#w~L(y1qqnSW2#%^EeA`wn(9Pv0*!4-MZ$a1u#_)gS{&nPTKb zfd}s=Jxn#W9vb*4ACkgHK0)RjKdFQ*@?H@LANxRMQmzN0s9mxRBzZW+u4iQ7*79xt zxKEUWAt9^`x^h}}jPh31W??PIt}a-|ktmdIfv~axL1iIuDRdG~Xw<=1n$@v=t7D52 zsT+4#8ESV#kvYUUwGhC0*aei9Djx_!aI(pT=g65f<#ZV`J|P;azT@&xvlS|VHdyQU2p4jD7; z;8Js=cmJv9+D+B|6O3xc$_;`xy>vENy!Pw0wtU4CSB{`4X9x{Lg^?z z>8KT7zKpPe9#@cex6iqDb~9(InNmZY)W)alnYTrcn7ti>KYJmT%6Zo^(9C0xHB$5* zBz9Zmuw@jqx{NZ`Hx5*1_~$=a6~mRMjPV55s(9oe)AAzBAF}4L<8x<3mmfx>#`jmK zcwly^h_Hp5zAvfWdg*;l^KyP|JdHRRG`Jl|a9y}r5_}b#!8rR`i~e$w8F$;U|Ykzr(iiw#2Ut52SVUk1GSmWawrU;6#80Q6fQ4IV>{8x#--5(DUP zKrHBwoSUPQyOr@TuJx*q+HA5TwPWt_K(*35RBuUyZ1-`;_=7j}fk^s)G!L%{S1OQJ z^luflZM$9w-K*GbJ6b*h@s2{E8dUPZ;aRo1iAyU8VOe`R!*zHlrB8DfHj)IFk8|B2 zi9Ay=N~%J=n!cWji33DYSVq7opqJuN+A_RfSL#%F#46c?C}CC3m<4eQ8&C(Rrs1jV z+}||>^`z>22cbzDs#mrl@6!Cjfj)Bn!Sx6&&0)qY4Q-!E3WnDAOM?q_1f80bD7Gq3 zKCGrFw|ni5&`)&Gl6uVbnbNlEs|qa_v5|xKJm9j0l%kbm0#&3JnMGTCKNiCAGjB2k zF3mGqC+wfK+vhJtWc^78;b-@SYt8AKs4G-dbjT^KFioZ}+MENi3+9E#tTJHU6%-Ol zBazj1+Y91^kH0?M?{DggSp*tG4_dRwfV7z?ElXjbfmC4PTh}qIXh&S9hR*CXUR`Csi+lgTU5OWs-@K>2~*849vICB|(M?uaPZ& z-#?C(s3Ko=_i=j)QDV&4Xa1lI7wHg0E9wm0V!CUO7y8-tBqon2fFE^Bl}658%p!r@ z%xY*FyD9hZ0_*#0AkhhOkRK6&==nk{={P*e=IXq--Afdd3?8PLe>uLL&tH)R;K^VX*M1mHeR;x^aD7hJQ|Kx%8 zF{Cj(1n5Q%W#^FsL6UW*`$Fm*6_lbBR?F1bQ|9UBmcO*K&3UN?#sPd1E~})h#|qLx zIX*j+`MX*w6Q@zwZhw(*71mj_%swb@MfB9un_UKTDB1=Ub(zGRP`7a2`hQpeJAaw248MgSMpFpY7cT zP{g|D3&LInNDi*+lWCi3pQ%4<1wbT1v^T?n91-6v5VlkakJqP?&esMq#T=RO!W0C~Fooc`|2Tjg$KezqYp8NfKyqT`H>Qz?|7_Yj6EmkM3%3pD;`xc{xT z;IU?w?gD5DAwW8Z^iOT^M~40%dC5OZKz^Mt|FKp7DgX(J?TP@@zKPy~z6iT{Ce8)J z2`h7uG^y@_8aymPET_iZqCI=o;u&{tZ`+M;vwwQz{eo+P5KlYv4J3HW5Yz1IndcH+ zhhR15!4EWOI^H<_Y5MjIP&qlN@PRlDblB=(MY3nC9-rRD>KN!RaeOVe$vl{8?oK93S*2)}!@i>--%4T*# zGA$Iyl3bnP`BbmHqXpY~lvQV7j%O<4xH#b-w`_@3R9fH%Ny-f$O-O`+c8k?fW_&1? zfib_%+SpQ=1)Z-gVpWjv(j2OkC;C!n9kq=c>`s^nj=5!aE_?ndmbvX&NK#rqnk6|l zZX_g)VOqC_eA9sB!;UZdJ55GdPi%yMP3ndh16a?pXLc&)A=UD)*}}l3 ziEL=c{b?WUe7p4wX6$nWf-Z>hTHgN%?A~_8s(E}*IUY#+$w@bM0E^&}`PE=Bgr|w) zAeGiT`WCth;2NAJ@#Cf@9+z=>fizdVuhaoU)<;9BCf5o%3{(9c>+Q=4oJcu{VV|<9 zK*GHnO^_^i5=d$za2h5V6+@W!`t9q-i~sMeZPK4vTYBXFZW z-XfgtTd@PDVOkDr}jysHm$aBKGe^Jn(X&Rf*$vv)2o6*E<5+mzns%c9{q*eDw#wBk~3 z)#FjtipQpRcbsCto51E%vX7_lIU7)^rZ8q}fDJpwJrVFQ8a?v|VL7X;vXuGcDE+mT zgk}#jeO_-EGn9S?mcZs(!*h9q%iE?B^3%G^S$lyaL7`Y=Pg4h(jHRWbV{HsH_~UdW z&0!;-%~qt_;)Ug*V~4}O{nzqA?V$3|f zUY}p0M$_Uz_jz9<8NIskT1%Sz#Z?~KYTew^x z%MTjZ*?N#pjTG8I7$^@5WG;5~df_azYBoFE4GqZR$4(?t8_yAh5I~e0w6z?qMYBJ= zgU7f&d*Q+;I59Cqt}0&<`GE?9dUZ|r?g0jS2KGl_|Jo@tFJuQoyb%)|d#^qBiJ^zt z@Jl2q&Bn&+L3Sw=dWHcRc#=U`kG%u?h^|uZ zBzt70Jy9-g(!b~}>-}a17zSJ!YVSv*8NbwEc z%ntUTP7UD>EUi2ZSpdgRbE5@;(!3Km8)yayxB&P_#0*Jc3JCa%PO9(3FJk?gr*b7( z+hu@4qsf>k+pJpn620J8(InuHj-`;}Iva18+fX8Tl~A$goBfkal&4$+aoQgZIw1Bm z-f|@ng>u<8#3|>@MSCI+<>`y4AUdN0e^50F+Vyb#4v+o7s2sI4#H7ApU|$3Zsq6E0 zY|gHg&>$&Mw_CVJS7SF`ei}Dfr?lOPCxhLTh$G!d+tYR@i&UpXZd`sKLkUD+h_sVi zvy*bhvb7tY6y8}`-(uq6_W(YYxdPRM@>VNV7TbHbEce+Wxi;DUnlx2ad|R83lw47P zHTT3i7W#X!d=uzrFN|J zoW>!>O@>tz@FlxDVm$4E;}aD_?>gZi~< z5)Z1FrSBNeE~~q`RR`{5GA*1V#=xMCt5lZ7+TksS>?_Zq1$O1Zf@R*j^9&U+&Qm=>u_0hpAqgMO(BhpTE;tyOU-ZmV}tcBp*}U2&5xdp@|SEWe_zw- zrF07k3ZREC;1|#(18ih#sNi60=g45_?C4}`{fC(dD6;=wi7)yGi zNHwwHR4yuc!n5hJm~>9up5gZI^ytYZ)uh(iuszbfQ$t_R&a--*tx(Noc@%k9v)1@B-eP;-g+At!$et40J(_+s12mQ z%8tIB-Twp!FlGOEWhQpn0CZ`{Dd-JC$Vv1M8>kQgHKsaT4~=RdNzYv)Zj5)*A>XkE zKO3jwLma`oEG?IJ5*XG*7ma01+mQK2JcuYAILtSd_eikc31yBu#OHG4*7+!hwOl{e za*&Wh=mx+Nz*dW+q&{rJ)<@;`eaE1ZCg(-ZGqJ%`O88O@oI$=wQ3z8FENAoMyMpT6 zx-;W4#QWh$-fZa~Ob;l-wKob1QRtZXueOuSX`f?H<{#OCEw3A_v)DTKlON_XuqahB zR|2X(gU&9tWC<2;vC5KEzt_0BsAeuvfBVtL+! zkBn@Svzmd?fovnq&U%s9yF3Ny9_-8uhLULe3mm44_8~h1ovGJ<4*IHJ#378V`rQtYHT93(+h|!d|)%OIJ!>W z@ntwq5W@qRyFwkFx0{gDyO)|T)bhiu;Z*lm$yd67b^A}x60%dxM+G45=773iD!VU!Fm7ply11Nf41Ai4B^!#MhBP%@bKlB+7#)Yyyc*_OWGBH7<<=J!E zr3^nJmSguvtRuZ-YG`RxdndQ?706^z4pbeZ(*7}s^@XZeB*|`bN-HZ4#vT*`UF~6+ z%8U#aEW_N4UK1P%N(*1;*Opz2|OJLru1mv9}OGS zx^YZl2;XA{qaa!1Eug)!L)fV2hXzIoG0x3%(%4Ri4tNFEK<9EWrS~}{OmpS4h(E2I+k?dNZOp>xMX@5;* zIGUef6Khvy6`Fpaj(C_#eKd-x0J}&@$WtitV0d^OG-Z-La%b!__A=`^_E$-t_IO_+ zBRZ?9#NJ8{j^riDu?x`)0?XCO*zTiIU{xtuhD=-KhATRaz4Zy$U^UuHj zrX5&8`riTmz47p`0E2)X?=NkLZwLN+`_*3uJ^@;1{(CdlTb#EIH@}gT0Ku;}?Kf`+ zzwJ@@Jy-|sUxWXnW8p2z+Y<2KD8hh@#6KS4uPDC?!r!92Eoc3W(uVT4D8GtZ-=e&& zSNn}Zg82*OZS~q)fVYJXzX7Q6|MoF{l|8&gd7B0OjiOBa3*|p@qHhu2CUt%z^ppLG z@UIll+o5kGyuXKDQNH~Oe;e<83-~r*`Wx_u>Mh`}b^aATeT(uo`0^X&jP7qyeuZM* zqWrrv@*4vP=p73X(BHTwZvp<@BK<3X0oz{y{$ZcK9sTb+{9i{aaQ=61", "wheel"] +build-backend = "setuptools.build_meta" + diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..1610f93 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,34 @@ +[metadata] +name=appPublic +version = 5.2.3 +description = Your project description +authors = [{ +author = "yu moqing" +author_email = "yumoqing@gmail.com" }] +readme = "README.md" +license = "MIT" +[options] +packages = find: +requires-python = ">=3.8" +install_requires = + xlrd + bs4 + xlwt + nanoid + psutil + ujson + numpy + rsa + brotli + aiohttp + asyncio + aiohttp + aiohttp_socks + requests + jinja2 + pyzmq + cryptography + bcrypt + asyncssh + eventpy +