mirror of
https://github.com/veops/cmdb.git
synced 2025-09-13 15:06:53 +08:00
Compare commits
529 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dc1a2a7632 | ||
|
153fef4918 | ||
|
e218f8e065 | ||
|
b7137b3975 | ||
|
c6a9478dbb | ||
|
af40004fe9 | ||
|
d86eb1c5eb | ||
|
fef3bfceaf | ||
|
3a4f0b248f | ||
|
ffa3d7cd43 | ||
|
6aefac98cd | ||
|
22989f8d5a | ||
|
ebe9d1e29f | ||
|
f1dd5ca074 | ||
|
4dd95f0d7e | ||
|
7e3e248c2b | ||
|
a5ff1139f7 | ||
|
00135f4644 | ||
|
8297d4c9b4 | ||
|
5143539593 | ||
|
a6eb2f0d21 | ||
|
07a63bef6e | ||
|
d69efeea25 | ||
|
0ef67360ad | ||
|
e2f993bc11 | ||
|
05d2795e79 | ||
|
6ff77a140c | ||
|
6503d32e6e | ||
|
887a69c2bd | ||
|
6d052eaffc | ||
|
d0f0bf84dd | ||
|
802fda66e7 | ||
|
c95747c88a | ||
|
ed49b238d8 | ||
|
375f0879fb | ||
|
8bc1893ca9 | ||
|
53cd2342bf | ||
|
eff6d974d4 | ||
|
8478d2f858 | ||
|
80e99cc335 | ||
|
8f64fc4aa0 | ||
|
cfc345c993 | ||
|
928116d0b5 | ||
|
4c2e6ae69f | ||
|
a2c75fd34e | ||
|
8217053abf | ||
|
8c17373e45 | ||
|
a8e2595327 | ||
|
dfbba103cd | ||
|
86b9d5a7f4 | ||
|
612922a1b7 | ||
|
2758c5e468 | ||
|
d85c86a839 | ||
|
8355137e43 | ||
|
2e644233bc | ||
|
d9b4082b46 | ||
|
a07f984152 | ||
|
4cab7ef6b0 | ||
|
070c163de6 | ||
|
282a779fb1 | ||
|
cb6b51a84c | ||
|
34bd320e75 | ||
|
1eca5791f6 | ||
|
13b1c9a30c | ||
|
b1a15a85d2 | ||
|
08e5a02caf | ||
|
308827b8fc | ||
|
dc4ccb22b9 | ||
|
c482e7ea43 | ||
|
663c14f763 | ||
|
c6ee227bab | ||
|
cb62cf2410 | ||
|
133f32a6b0 | ||
|
45c48c86fe | ||
|
2321f17dae | ||
|
ddb31a07a2 | ||
|
b474914fbb | ||
|
26099a3d69 | ||
|
62829c885b | ||
|
260aed6462 | ||
|
3841999cca | ||
|
14c03ce5d2 | ||
|
f463ecd6e6 | ||
|
adc0cfd5c5 | ||
|
086481657e | ||
|
d2f84ae3dc | ||
|
9f1b510cb3 | ||
|
61acb2483d | ||
|
0196c8a82c | ||
|
bed2323fc1 | ||
|
be9b308f56 | ||
|
8ba658ea1b | ||
|
0aa668cfa0 | ||
|
e20fd33a53 | ||
|
7462de63de | ||
|
5f9ba069ad | ||
|
5dc0d95ff8 | ||
|
e5536b76e6 | ||
|
8b044efd4e | ||
|
747b5bf494 | ||
|
21067022f6 | ||
|
4102c44fb2 | ||
|
600f95ce18 | ||
|
950fd38044 | ||
|
01085615b5 | ||
|
734f1940f9 | ||
|
c25c1e4e4b | ||
|
826a8306d3 | ||
|
740aae573e | ||
|
17828a7631 | ||
|
02cb497bdc | ||
|
05a7dc41ee | ||
|
459c70ba2d | ||
|
774f42ac34 | ||
|
420029a5e2 | ||
|
ab8acbfd20 | ||
|
4468b6a8de | ||
|
6bf145d085 | ||
|
42b1e47e76 | ||
|
673134003a | ||
|
ef67885571 | ||
|
075bf7217f | ||
|
3b7b8f435c | ||
|
2b7f6aeef3 | ||
|
544fac8aca | ||
|
3d0a56ec8c | ||
|
d2d8482052 | ||
|
a0afae8d2e | ||
|
9f3da68636 | ||
|
24b955c288 | ||
|
a07b2d37ec | ||
|
c86fcb4e7b | ||
|
ca7964f24b | ||
|
c42ac634fb | ||
|
a6fc3341ce | ||
|
fc3f2e25f3 | ||
|
511a5f70c6 | ||
|
f8ff4d5e45 | ||
|
3ab72cceaf | ||
|
4ab7e3c70c | ||
|
a7fe75f7df | ||
|
3474a71a75 | ||
|
6531baff64 | ||
|
ed5936250f | ||
|
52c32e2ab1 | ||
|
d3224625b6 | ||
|
f158c7e33a | ||
|
6dc12bb6ac | ||
|
b33ae16c00 | ||
|
2caffc2670 | ||
|
f28af51007 | ||
|
3a0369559f | ||
|
a74a2c5a94 | ||
|
9fbcb2838e | ||
|
60a445b972 | ||
|
bfdd7b6a0e | ||
|
ab093d2493 | ||
|
315a578a31 | ||
|
1e16dc5e5b | ||
|
f67e196acf | ||
|
439e25d5dd | ||
|
ea59c0d71f | ||
|
1137127aab | ||
|
4ad1b5282e | ||
|
cdd5e4d9aa | ||
|
432de5e847 | ||
|
3a2339765a | ||
|
b5a2af7420 | ||
|
8b267613d6 | ||
|
b365eb27f6 | ||
|
2125f020b5 | ||
|
ea762e35a0 | ||
|
f11aadf6d4 | ||
|
9cbf133b9f | ||
|
95e8f9de74 | ||
|
26792147ae | ||
|
4f9b581c2e | ||
|
e2b1cb3003 | ||
|
f75a85b48a | ||
|
313fc80e54 | ||
|
e0666689e5 | ||
|
7a9fd4f9d6 | ||
|
2fd706be85 | ||
|
3df51bb670 | ||
|
9bbbcbe6dc | ||
|
16d6b40e8d | ||
|
ef2d3812a2 | ||
|
bc653efd04 | ||
|
d891d7365d | ||
|
9953b2fc98 | ||
|
8de54812dc | ||
|
eb7d52cf35 | ||
|
6c4a5f2f6b | ||
|
17c5d4538b | ||
|
6c3e3f9eed | ||
|
b0494adc17 | ||
|
fc133f2ae9 | ||
|
ac6e3a0318 | ||
|
404ec976cc | ||
|
4211bbcbc9 | ||
|
0158636671 | ||
|
d986bc3bbc | ||
|
044b820548 | ||
|
536daa6d4f | ||
|
b0620b043b | ||
|
a88c9cf7f7 | ||
|
be50f505d1 | ||
|
0bb4f633d6 | ||
|
78b521f3af | ||
|
77bc850d4a | ||
|
e52f201ba1 | ||
|
64aea424dc | ||
|
0655b0e9eb | ||
|
cce0649299 | ||
|
52574c64cc | ||
|
fb904b01a6 | ||
|
63af79ec45 | ||
|
38af86317a | ||
|
03bac86588 | ||
|
130b68cadd | ||
|
65000f8141 | ||
|
23692ad50b | ||
|
16cd34e8b8 | ||
|
985f67ee47 | ||
|
8d95f8d57d | ||
|
cf6230008d | ||
|
ec97fa84d8 | ||
|
76f074704b | ||
|
e5addab3af | ||
|
1c6be9e281 | ||
|
9552892c68 | ||
|
b59e1af318 | ||
|
d164d883ab | ||
|
1fef160d9e | ||
|
2e537d390a | ||
|
5b9fe15afa | ||
|
89fa5f2243 | ||
|
652a5c7fb8 | ||
|
afb6adec89 | ||
|
a9db4285ab | ||
|
a04bdc29a5 | ||
|
91e0e076a7 | ||
|
339a7b857e | ||
|
e86e5ad1fd | ||
|
c50a69de77 | ||
|
4d16e9e6d9 | ||
|
fcea4dcb9f | ||
|
f98fd24c62 | ||
|
f10eeb8439 | ||
|
f070948122 | ||
|
4112bcf547 | ||
|
2292756bf7 | ||
|
93e2483974 | ||
|
fbb4fcc255 | ||
|
fc77241006 | ||
|
0d04ad7d90 | ||
|
e6290e49ea | ||
|
97aa2e0ebe | ||
|
939d9dc3cd | ||
|
576d2e3bc4 | ||
|
9a40246d29 | ||
|
044f95c3be | ||
|
a386de355e | ||
|
b93afc1790 | ||
|
77d89677ef | ||
|
7ec6775f03 | ||
|
98cc853dbc | ||
|
f57ff80099 | ||
|
51e4b5dd8f | ||
|
dbf44a020b | ||
|
8e578797ef | ||
|
158de4b946 | ||
|
3cf234d49e | ||
|
a7debc1b3b | ||
|
9268da2ffa | ||
|
cfcb092478 | ||
|
0d8b41b64a | ||
|
d85715793f | ||
|
afbdbe4682 | ||
|
e629abebb7 | ||
|
029c12365a | ||
|
4d000d9805 | ||
|
f1fc66bd2c | ||
|
d6af4af1d1 | ||
|
7fe2bdca5f | ||
|
1432131d2b | ||
|
bc94d039f5 | ||
|
5abafed9c8 | ||
|
04e249feac | ||
|
ef3e6bc6b0 | ||
|
d9d5f8f818 | ||
|
578da0807c | ||
|
3eb35f5497 | ||
|
9669ad04cd | ||
|
70214807ca | ||
|
7c1c309f7a | ||
|
9b9799ff5e | ||
|
b2578b61fa | ||
|
619f47ae13 | ||
|
37c5e31799 | ||
|
ab70b2a655 | ||
|
c285606f4a | ||
|
6d3611bd73 | ||
|
764f6a07e0 | ||
|
ae8d487af4 | ||
|
87c6554555 | ||
|
f5671c2a2a | ||
|
43ad3dfa7b | ||
|
29fa17a0b8 | ||
|
5191d6ed73 | ||
|
8348f8e7b1 | ||
|
75c48a0807 | ||
|
5b38385f7e | ||
|
036e1d236b | ||
|
c31be0f753 | ||
|
764d2fac3f | ||
|
f4079e9c3e | ||
|
2a0ed72235 | ||
|
9e803ae4c7 | ||
|
bebdb61adf | ||
|
f49cad771b | ||
|
a5b4fbda40 | ||
|
2cce2d5cf2 | ||
|
e720b7af66 | ||
|
09e4a5111b | ||
|
3539b12503 | ||
|
21d8673b5d | ||
|
7154426dc7 | ||
|
ca75c7dcd0 | ||
|
194a2254a6 | ||
|
26abad14d0 | ||
|
1521a71f9c | ||
|
d425b455f1 | ||
|
230307474b | ||
|
69d6b40e39 | ||
|
5dc2f89e7f | ||
|
9eaca4d6a0 | ||
|
3680a462f5 | ||
|
3ac50e7cd8 | ||
|
21b2cc1d5d | ||
|
cd5448cc7d | ||
|
10610bdb4b | ||
|
b5c2156387 | ||
|
b05ae0d1a7 | ||
|
bbf6138d43 | ||
|
1ba3e6a680 | ||
|
64045c1f93 | ||
|
5a3e55813c | ||
|
bc72e58886 | ||
|
9e78955ba1 | ||
|
136853d9a4 | ||
|
036e3ad00d | ||
|
5ce6c93237 | ||
|
43dba7f7ed | ||
|
f4879d20d6 | ||
|
740e4c6034 | ||
|
0f2baa1d94 | ||
|
405b0af72c | ||
|
a4e5178979 | ||
|
c14fe23283 | ||
|
b3a058f908 | ||
|
bd82a0e27c | ||
|
f22a5c3543 | ||
|
ed81c3f091 | ||
|
07814b85f9 | ||
|
db52b28d6b | ||
|
fc85ba21c8 | ||
|
6c5ee3fcd9 | ||
|
40f1ef88a9 | ||
|
bce422ffc8 | ||
|
7c79066532 | ||
|
1129ac93fb | ||
|
5ab0e7e737 | ||
|
23319c7417 | ||
|
c74f85cabb | ||
|
fce2b689fb | ||
|
105327bb0c | ||
|
745c43d0a4 | ||
|
3130d94568 | ||
|
04a66eb239 | ||
|
68390ec6f1 | ||
|
17392be138 | ||
|
f2fdb29221 | ||
|
4a18698423 | ||
|
95ccee04f9 | ||
|
b60628247b | ||
|
a6d7699ab4 | ||
|
4b21bcc438 | ||
|
33dce2f0f3 | ||
|
d43b827fe5 | ||
|
aec8bade41 | ||
|
89ae89a449 | ||
|
945f90e386 | ||
|
2ba6a16613 | ||
|
6089039366 | ||
|
e1e5307084 | ||
|
2ff7fce9dd | ||
|
fc4d3e0c1a | ||
|
f66a94712e | ||
|
24664c7686 | ||
|
1d668bab6e | ||
|
3d4b84909e | ||
|
8341e742eb | ||
|
a71ba83de0 | ||
|
9668131c18 | ||
|
4a744dcad9 | ||
|
2a420225e2 | ||
|
ff67785618 | ||
|
dfe1ba55d5 | ||
|
90b1b6b7af | ||
|
d5fbe42ed7 | ||
|
f424ad6864 | ||
|
16b724bd40 | ||
|
f70ed54cad | ||
|
dd64564160 | ||
|
cc2cdbcc9f | ||
|
81fe850627 | ||
|
487d9f76f6 | ||
|
92dd4c5dfe | ||
|
8ee7c6daf8 | ||
|
882b158d18 | ||
|
85222443c0 | ||
|
1696ecf49d | ||
|
73b92ff533 | ||
|
e977bb15a5 | ||
|
7c46d6cdbf | ||
|
4d11c1f7db | ||
|
0a563deb11 | ||
|
ba80ec4403 | ||
|
3b7cc4595b | ||
|
9fe47657a6 | ||
|
5a4a6caa07 | ||
|
9dadbe1599 | ||
|
40d016f513 | ||
|
655edaa7c8 | ||
|
7fa5cff919 | ||
|
d19834ed5d | ||
|
b6be430aa3 | ||
|
63792c242f | ||
|
10f7029722 | ||
|
ba176542dc | ||
|
aae3b6e2ff | ||
|
b370c7d46e | ||
|
efa5a8ea5d | ||
|
fd532626ac | ||
|
617337c614 | ||
|
9a3d24ac81 | ||
|
454dd4c56b | ||
|
88ad72d4dc | ||
|
8d1517d550 | ||
|
d3a8ef5966 | ||
|
e5baa5012d | ||
|
a1f63b00dd | ||
|
47ded84231 | ||
|
224a48a5f3 | ||
|
0e7c52df71 | ||
|
ff701cc770 | ||
|
6a7bb725cc | ||
|
0a13186c13 | ||
|
a0ffeb9950 | ||
|
6c70ec6d53 | ||
|
4b5f82699a | ||
|
f78c3b928b | ||
|
332659c1d5 | ||
|
3beb2706dc | ||
|
a14111e1ce | ||
|
c4320c14f9 | ||
|
4c5442748f | ||
|
a81750acba | ||
|
0439e2462b | ||
|
3b62bd7ac9 | ||
|
f6add52721 | ||
|
c85e535288 | ||
|
c0c6d116b5 | ||
|
39153e92d1 | ||
|
42bcc2e510 | ||
|
398fbb25dc | ||
|
4b312d4f99 | ||
|
10414155a5 | ||
|
feda0c37e7 | ||
|
173c120b64 | ||
|
5f2a0d1a7b | ||
|
50f894a01d | ||
|
66e93e73af | ||
|
58ad9d3f05 | ||
|
08c96039e9 | ||
|
ca0dd97626 | ||
|
7810ee3974 | ||
|
2cfea7ef08 | ||
|
0cee6cea25 | ||
|
5d13ba2f26 | ||
|
a583433530 | ||
|
733ac3b2b4 | ||
|
ef6300255a | ||
|
aad37dcf0b | ||
|
cce10d39ea | ||
|
c521dd447e | ||
|
4d0cd4ba56 | ||
|
7291274cb1 | ||
|
44f2e383c3 | ||
|
1f8219b418 | ||
|
cb2f170ded | ||
|
1241a23ba8 | ||
|
7d7744b7dc | ||
|
9c7d51127a | ||
|
b5a987f6b4 | ||
|
7bbc68bfd5 | ||
|
99d11e11ce | ||
|
7b96ac4638 | ||
|
0a36330852 | ||
|
9105f92c82 | ||
|
57541ab486 | ||
|
a0fcbd220e | ||
|
d54b404eb6 | ||
|
620c5bb5eb | ||
|
0fde1d699d | ||
|
61f77cf311 | ||
|
13476128d5 | ||
|
5cdb4ecd2a | ||
|
64c3b9da3b | ||
|
55dad7a58c | ||
|
38dabc35e5 | ||
|
5b4f95a50e | ||
|
f3046d3c91 | ||
|
5faae9af67 | ||
|
c0b50642e0 | ||
|
12ca296879 | ||
|
420c6cea2b | ||
|
ccc4bb48fa |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -70,7 +70,6 @@ settings.py
|
|||||||
# UI
|
# UI
|
||||||
cmdb-ui/node_modules
|
cmdb-ui/node_modules
|
||||||
cmdb-ui/dist
|
cmdb-ui/dist
|
||||||
cmdb-ui/yarn.lock
|
|
||||||
|
|
||||||
# Log files
|
# Log files
|
||||||
cmdb-ui/npm-debug.log*
|
cmdb-ui/npm-debug.log*
|
||||||
|
24
README.md
24
README.md
@@ -73,33 +73,19 @@
|
|||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
### Docker 一键快速构建
|
### Docker 一键快速构建
|
||||||
> 方法一
|
- 进入主目录(先安装 docker 环境, 注意要clone整个项目)
|
||||||
- 第一步: 先安装 docker 环境, 以及docker-compose
|
|
||||||
- 第二步: 拷贝项目
|
|
||||||
```shell
|
|
||||||
git clone https://github.com/veops/cmdb.git
|
|
||||||
```
|
|
||||||
- 第三步:进入主目录,执行:
|
|
||||||
```
|
```
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
> 方法二, 该方法适用于linux系统
|
|
||||||
- 第一步: 先安装 docker 环境, 以及docker-compose
|
|
||||||
- 第二步: 直接使用项目根目录下的install.sh 文件进行 `安装`、`启动`、`暂停`、`查状态`、`删除`、`卸载`
|
|
||||||
```shell
|
|
||||||
curl -so install.sh https://raw.githubusercontent.com/veops/cmdb/master/install.sh
|
|
||||||
sh install.sh install
|
|
||||||
```
|
|
||||||
|
|
||||||
### [本地开发环境搭建](docs/local.md)
|
|
||||||
|
|
||||||
### [Makefile 安装](docs/makefile.md)
|
|
||||||
|
|
||||||
## 验证
|
|
||||||
- 浏览器打开: [http://127.0.0.1:8000](http://127.0.0.1:8000)
|
- 浏览器打开: [http://127.0.0.1:8000](http://127.0.0.1:8000)
|
||||||
- username: demo 或者 admin
|
- username: demo 或者 admin
|
||||||
- password: 123456
|
- password: 123456
|
||||||
|
|
||||||
|
### [本地开发环境搭建](docs/local.md)
|
||||||
|
|
||||||
|
### [Makefile 安装](docs/makefile.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ name = "pypi"
|
|||||||
[packages]
|
[packages]
|
||||||
# Flask
|
# Flask
|
||||||
Flask = "==2.3.2"
|
Flask = "==2.3.2"
|
||||||
Werkzeug = ">=2.3.6"
|
Werkzeug = "==2.3.6"
|
||||||
click = ">=5.0"
|
click = ">=5.0"
|
||||||
# Api
|
# Api
|
||||||
Flask-RESTful = "==0.3.10"
|
Flask-RESTful = "==0.3.10"
|
||||||
@@ -21,7 +21,7 @@ Flask-Migrate = "==2.5.2"
|
|||||||
gunicorn = "==21.0.1"
|
gunicorn = "==21.0.1"
|
||||||
supervisor = "==4.0.3"
|
supervisor = "==4.0.3"
|
||||||
# Auth
|
# Auth
|
||||||
Flask-Login = ">=0.6.2"
|
Flask-Login = "==0.6.2"
|
||||||
Flask-Bcrypt = "==1.0.1"
|
Flask-Bcrypt = "==1.0.1"
|
||||||
Flask-Cors = ">=3.0.8"
|
Flask-Cors = ">=3.0.8"
|
||||||
ldap3 = "==2.9.1"
|
ldap3 = "==2.9.1"
|
||||||
@@ -43,7 +43,7 @@ WTForms = "==3.0.0"
|
|||||||
email-validator = "==1.3.1"
|
email-validator = "==1.3.1"
|
||||||
treelib = "==1.6.1"
|
treelib = "==1.6.1"
|
||||||
flasgger = "==0.9.5"
|
flasgger = "==0.9.5"
|
||||||
Pillow = ">=10.0.1"
|
Pillow = "==9.3.0"
|
||||||
# other
|
# other
|
||||||
six = "==1.16.0"
|
six = "==1.16.0"
|
||||||
bs4 = ">=0.0.1"
|
bs4 = ">=0.0.1"
|
||||||
@@ -62,7 +62,6 @@ alembic = "==1.7.7"
|
|||||||
hvac = "==2.0.0"
|
hvac = "==2.0.0"
|
||||||
colorama = ">=0.4.6"
|
colorama = ">=0.4.6"
|
||||||
pycryptodomex = ">=3.19.0"
|
pycryptodomex = ">=3.19.0"
|
||||||
lz4 = ">=4.3.2"
|
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
# Testing
|
# Testing
|
||||||
|
@@ -19,8 +19,7 @@ from flask.json.provider import DefaultJSONProvider
|
|||||||
import api.views.entry
|
import api.views.entry
|
||||||
from api.extensions import (bcrypt, cache, celery, cors, db, es, login_manager, migrate, rd)
|
from api.extensions import (bcrypt, cache, celery, cors, db, es, login_manager, migrate, rd)
|
||||||
from api.extensions import inner_secrets
|
from api.extensions import inner_secrets
|
||||||
from api.lib.perm.authentication.cas import CAS
|
from api.flask_cas import CAS
|
||||||
from api.lib.perm.authentication.oauth2 import OAuth2
|
|
||||||
from api.lib.secrets.secrets import InnerKVManger
|
from api.lib.secrets.secrets import InnerKVManger
|
||||||
from api.models.acl import User
|
from api.models.acl import User
|
||||||
|
|
||||||
@@ -97,7 +96,6 @@ def create_app(config_object="settings"):
|
|||||||
register_shell_context(app)
|
register_shell_context(app)
|
||||||
register_commands(app)
|
register_commands(app)
|
||||||
CAS(app)
|
CAS(app)
|
||||||
OAuth2(app)
|
|
||||||
app.wsgi_app = ReverseProxy(app.wsgi_app)
|
app.wsgi_app = ReverseProxy(app.wsgi_app)
|
||||||
configure_upload_dir(app)
|
configure_upload_dir(app)
|
||||||
|
|
||||||
@@ -194,11 +192,10 @@ def configure_logger(app):
|
|||||||
app.logger.addHandler(handler)
|
app.logger.addHandler(handler)
|
||||||
|
|
||||||
log_file = app.config['LOG_PATH']
|
log_file = app.config['LOG_PATH']
|
||||||
if log_file and log_file != "/dev/stdout":
|
file_handler = RotatingFileHandler(log_file,
|
||||||
file_handler = RotatingFileHandler(log_file,
|
maxBytes=2 ** 30,
|
||||||
maxBytes=2 ** 30,
|
backupCount=7)
|
||||||
backupCount=7)
|
file_handler.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
||||||
file_handler.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
file_handler.setFormatter(formatter)
|
||||||
file_handler.setFormatter(formatter)
|
app.logger.addHandler(file_handler)
|
||||||
app.logger.addHandler(file_handler)
|
|
||||||
app.logger.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
app.logger.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
import click
|
import click
|
||||||
from flask.cli import with_appcontext
|
from flask.cli import with_appcontext
|
||||||
|
|
||||||
from api.lib.perm.acl.user import UserCRUD
|
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
@@ -25,18 +23,50 @@ def init_acl():
|
|||||||
role_rebuild.apply_async(args=(role.id, app.id), queue=ACL_QUEUE)
|
role_rebuild.apply_async(args=(role.id, app.id), queue=ACL_QUEUE)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
# @click.command()
|
||||||
@with_appcontext
|
# @with_appcontext
|
||||||
def add_user():
|
# def acl_clean():
|
||||||
"""
|
# from api.models.acl import Resource
|
||||||
create a user
|
# from api.models.acl import Permission
|
||||||
|
# from api.models.acl import RolePermission
|
||||||
is_admin: default is False
|
#
|
||||||
|
# perms = RolePermission.get_by(to_dict=False)
|
||||||
"""
|
#
|
||||||
|
# for r in perms:
|
||||||
username = click.prompt('Enter username', confirmation_prompt=False)
|
# perm = Permission.get_by_id(r.perm_id)
|
||||||
password = click.prompt('Enter password', hide_input=True, confirmation_prompt=True)
|
# if perm and perm.app_id != r.app_id:
|
||||||
email = click.prompt('Enter email ', confirmation_prompt=False)
|
# resource_id = r.resource_id
|
||||||
|
# resource = Resource.get_by_id(resource_id)
|
||||||
UserCRUD.add(username=username, password=password, email=email)
|
# perm_name = perm.name
|
||||||
|
# existed = Permission.get_by(resource_type_id=resource.resource_type_id, name=perm_name, first=True,
|
||||||
|
# to_dict=False)
|
||||||
|
# if existed is not None:
|
||||||
|
# other = RolePermission.get_by(rid=r.rid, perm_id=existed.id, resource_id=resource_id)
|
||||||
|
# if not other:
|
||||||
|
# r.update(perm_id=existed.id)
|
||||||
|
# else:
|
||||||
|
# r.soft_delete()
|
||||||
|
# else:
|
||||||
|
# r.soft_delete()
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# @click.command()
|
||||||
|
# @with_appcontext
|
||||||
|
# def acl_has_resource_role():
|
||||||
|
# from api.models.acl import Role
|
||||||
|
# from api.models.acl import App
|
||||||
|
# from api.lib.perm.acl.cache import HasResourceRoleCache
|
||||||
|
# from api.lib.perm.acl.role import RoleCRUD
|
||||||
|
#
|
||||||
|
# roles = Role.get_by(to_dict=False)
|
||||||
|
# apps = App.get_by(to_dict=False)
|
||||||
|
# for role in roles:
|
||||||
|
# if role.app_id:
|
||||||
|
# res = RoleCRUD.recursive_resources(role.id, role.app_id)
|
||||||
|
# if res.get('resources') or res.get('groups'):
|
||||||
|
# HasResourceRoleCache.add(role.id, role.app_id)
|
||||||
|
# else:
|
||||||
|
# for app in apps:
|
||||||
|
# res = RoleCRUD.recursive_resources(role.id, app.id)
|
||||||
|
# if res.get('resources') or res.get('groups'):
|
||||||
|
# HasResourceRoleCache.add(role.id, app.id)
|
||||||
|
@@ -19,7 +19,6 @@ from api.lib.cmdb.cache import AttributeCache
|
|||||||
from api.lib.cmdb.const import PermEnum
|
from api.lib.cmdb.const import PermEnum
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
|
|
||||||
from api.lib.cmdb.const import ResourceTypeEnum
|
from api.lib.cmdb.const import ResourceTypeEnum
|
||||||
from api.lib.cmdb.const import RoleEnum
|
from api.lib.cmdb.const import RoleEnum
|
||||||
from api.lib.cmdb.const import ValueTypeEnum
|
from api.lib.cmdb.const import ValueTypeEnum
|
||||||
@@ -30,6 +29,7 @@ from api.lib.perm.acl.cache import AppCache
|
|||||||
from api.lib.perm.acl.resource import ResourceCRUD
|
from api.lib.perm.acl.resource import ResourceCRUD
|
||||||
from api.lib.perm.acl.resource import ResourceTypeCRUD
|
from api.lib.perm.acl.resource import ResourceTypeCRUD
|
||||||
from api.lib.perm.acl.role import RoleCRUD
|
from api.lib.perm.acl.role import RoleCRUD
|
||||||
|
from api.lib.perm.acl.user import UserCRUD
|
||||||
from api.lib.secrets.inner import KeyManage
|
from api.lib.secrets.inner import KeyManage
|
||||||
from api.lib.secrets.inner import global_key_threshold
|
from api.lib.secrets.inner import global_key_threshold
|
||||||
from api.lib.secrets.secrets import InnerKVManger
|
from api.lib.secrets.secrets import InnerKVManger
|
||||||
@@ -50,17 +50,12 @@ def cmdb_init_cache():
|
|||||||
|
|
||||||
ci_relations = CIRelation.get_by(to_dict=False)
|
ci_relations = CIRelation.get_by(to_dict=False)
|
||||||
relations = dict()
|
relations = dict()
|
||||||
relations2 = dict()
|
|
||||||
for cr in ci_relations:
|
for cr in ci_relations:
|
||||||
relations.setdefault(cr.first_ci_id, {}).update({cr.second_ci_id: cr.second_ci.type_id})
|
relations.setdefault(cr.first_ci_id, {}).update({cr.second_ci_id: cr.second_ci.type_id})
|
||||||
if cr.ancestor_ids:
|
|
||||||
relations2.setdefault(cr.ancestor_ids, {}).update({cr.second_ci_id: cr.second_ci.type_id})
|
|
||||||
for i in relations:
|
for i in relations:
|
||||||
relations[i] = json.dumps(relations[i])
|
relations[i] = json.dumps(relations[i])
|
||||||
if relations:
|
if relations:
|
||||||
rd.create_or_update(relations, REDIS_PREFIX_CI_RELATION)
|
rd.create_or_update(relations, REDIS_PREFIX_CI_RELATION)
|
||||||
if relations2:
|
|
||||||
rd.create_or_update(relations2, REDIS_PREFIX_CI_RELATION2)
|
|
||||||
|
|
||||||
es = None
|
es = None
|
||||||
if current_app.config.get("USE_ES"):
|
if current_app.config.get("USE_ES"):
|
||||||
@@ -133,10 +128,10 @@ def cmdb_init_acl():
|
|||||||
|
|
||||||
# 3. add resource and grant
|
# 3. add resource and grant
|
||||||
ci_types = CIType.get_by(to_dict=False)
|
ci_types = CIType.get_by(to_dict=False)
|
||||||
resource_type_id = ResourceType.get_by(name=ResourceTypeEnum.CI, first=True, to_dict=False).id
|
type_id = ResourceType.get_by(name=ResourceTypeEnum.CI, first=True, to_dict=False).id
|
||||||
for ci_type in ci_types:
|
for ci_type in ci_types:
|
||||||
try:
|
try:
|
||||||
ResourceCRUD.add(ci_type.name, resource_type_id, app_id)
|
ResourceCRUD.add(ci_type.name, type_id, app_id)
|
||||||
except AbortException:
|
except AbortException:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -146,10 +141,10 @@ def cmdb_init_acl():
|
|||||||
[PermEnum.READ])
|
[PermEnum.READ])
|
||||||
|
|
||||||
relation_views = PreferenceRelationView.get_by(to_dict=False)
|
relation_views = PreferenceRelationView.get_by(to_dict=False)
|
||||||
resource_type_id = ResourceType.get_by(name=ResourceTypeEnum.RELATION_VIEW, first=True, to_dict=False).id
|
type_id = ResourceType.get_by(name=ResourceTypeEnum.RELATION_VIEW, first=True, to_dict=False).id
|
||||||
for view in relation_views:
|
for view in relation_views:
|
||||||
try:
|
try:
|
||||||
ResourceCRUD.add(view.name, resource_type_id, app_id)
|
ResourceCRUD.add(view.name, type_id, app_id)
|
||||||
except AbortException:
|
except AbortException:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -159,6 +154,57 @@ def cmdb_init_acl():
|
|||||||
[PermEnum.READ])
|
[PermEnum.READ])
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
'-u',
|
||||||
|
'--user',
|
||||||
|
help='username'
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-p',
|
||||||
|
'--password',
|
||||||
|
help='password'
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-m',
|
||||||
|
'--mail',
|
||||||
|
help='mail'
|
||||||
|
)
|
||||||
|
@with_appcontext
|
||||||
|
def add_user(user, password, mail):
|
||||||
|
"""
|
||||||
|
create a user
|
||||||
|
|
||||||
|
is_admin: default is False
|
||||||
|
|
||||||
|
Example: flask add-user -u <username> -p <password> -m <mail>
|
||||||
|
"""
|
||||||
|
assert user is not None
|
||||||
|
assert password is not None
|
||||||
|
assert mail is not None
|
||||||
|
UserCRUD.add(username=user, password=password, email=mail)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
'-u',
|
||||||
|
'--user',
|
||||||
|
help='username'
|
||||||
|
)
|
||||||
|
@with_appcontext
|
||||||
|
def del_user(user):
|
||||||
|
"""
|
||||||
|
delete a user
|
||||||
|
|
||||||
|
Example: flask del-user -u <username>
|
||||||
|
"""
|
||||||
|
assert user is not None
|
||||||
|
from api.models.acl import User
|
||||||
|
|
||||||
|
u = User.get_by(username=user, first=True, to_dict=False)
|
||||||
|
u and UserCRUD.delete(u.uid)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
def cmdb_counter():
|
def cmdb_counter():
|
||||||
@@ -283,6 +329,7 @@ def valid_address(address):
|
|||||||
}
|
}
|
||||||
KeyManage.print_response(response)
|
KeyManage.print_response(response)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -397,7 +444,6 @@ def cmdb_password_data_migrate():
|
|||||||
|
|
||||||
value_table = CIIndexValueText if attr.is_index else CIValueText
|
value_table = CIIndexValueText if attr.is_index else CIValueText
|
||||||
|
|
||||||
failed = False
|
|
||||||
for i in value_table.get_by(attr_id=attr.id, to_dict=False):
|
for i in value_table.get_by(attr_id=attr.id, to_dict=False):
|
||||||
if current_app.config.get("SECRETS_ENGINE", 'inner') == 'inner':
|
if current_app.config.get("SECRETS_ENGINE", 'inner') == 'inner':
|
||||||
_, status = InnerCrypt().decrypt(i.value)
|
_, status = InnerCrypt().decrypt(i.value)
|
||||||
@@ -408,7 +454,6 @@ def cmdb_password_data_migrate():
|
|||||||
if status:
|
if status:
|
||||||
CIValueText.create(ci_id=i.ci_id, attr_id=attr.id, value=encrypt_value)
|
CIValueText.create(ci_id=i.ci_id, attr_id=attr.id, value=encrypt_value)
|
||||||
else:
|
else:
|
||||||
failed = True
|
|
||||||
continue
|
continue
|
||||||
elif current_app.config.get("SECRETS_ENGINE") == 'vault':
|
elif current_app.config.get("SECRETS_ENGINE") == 'vault':
|
||||||
if i.value == '******':
|
if i.value == '******':
|
||||||
@@ -419,48 +464,8 @@ def cmdb_password_data_migrate():
|
|||||||
vault.update("/{}/{}".format(i.ci_id, i.attr_id), dict(v=i.value))
|
vault.update("/{}/{}".format(i.ci_id, i.attr_id), dict(v=i.value))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('save password to vault failed: {}'.format(e))
|
print('save password to vault failed: {}'.format(e))
|
||||||
failed = True
|
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
i.delete()
|
i.delete()
|
||||||
|
|
||||||
if not failed and attr.is_index:
|
|
||||||
attr.update(is_index=False)
|
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
|
||||||
@with_appcontext
|
|
||||||
def cmdb_agent_init():
|
|
||||||
"""
|
|
||||||
Initialize the agent's permissions and obtain the key and secret
|
|
||||||
"""
|
|
||||||
|
|
||||||
from api.models.acl import User
|
|
||||||
|
|
||||||
user = User.get_by(username="cmdb_agent", first=True, to_dict=False)
|
|
||||||
if user is None:
|
|
||||||
click.echo(
|
|
||||||
click.style('user cmdb_agent does not exist, please use flask add-user to create it first', fg='red'))
|
|
||||||
return
|
|
||||||
|
|
||||||
# grant
|
|
||||||
_app = AppCache.get('cmdb') or App.create(name='cmdb')
|
|
||||||
app_id = _app.id
|
|
||||||
|
|
||||||
ci_types = CIType.get_by(to_dict=False)
|
|
||||||
resource_type_id = ResourceType.get_by(name=ResourceTypeEnum.CI, first=True, to_dict=False).id
|
|
||||||
for ci_type in ci_types:
|
|
||||||
try:
|
|
||||||
ResourceCRUD.add(ci_type.name, resource_type_id, app_id)
|
|
||||||
except AbortException:
|
|
||||||
pass
|
|
||||||
|
|
||||||
ACLManager().grant_resource_to_role(ci_type.name,
|
|
||||||
"cmdb_agent",
|
|
||||||
ResourceTypeEnum.CI,
|
|
||||||
[PermEnum.READ, PermEnum.UPDATE, PermEnum.ADD, PermEnum.DELETE])
|
|
||||||
|
|
||||||
click.echo("Key : {}".format(click.style(user.key, bg='red')))
|
|
||||||
click.echo("Secret: {}".format(click.style(user.secret, bg='red')))
|
|
||||||
|
@@ -10,6 +10,9 @@ from api.models.common_setting import Employee, Department
|
|||||||
|
|
||||||
|
|
||||||
class InitEmployee(object):
|
class InitEmployee(object):
|
||||||
|
"""
|
||||||
|
初始化员工
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log = current_app.logger
|
self.log = current_app.logger
|
||||||
@@ -55,8 +58,7 @@ class InitEmployee(object):
|
|||||||
self.log.error(ErrFormat.acl_import_user_failed.format(user['username'], str(e)))
|
self.log.error(ErrFormat.acl_import_user_failed.format(user['username'], str(e)))
|
||||||
self.log.error(e)
|
self.log.error(e)
|
||||||
|
|
||||||
@staticmethod
|
def get_rid_by_uid(self, uid):
|
||||||
def get_rid_by_uid(uid):
|
|
||||||
from api.models.acl import Role
|
from api.models.acl import Role
|
||||||
role = Role.get_by(first=True, uid=uid)
|
role = Role.get_by(first=True, uid=uid)
|
||||||
return role['id'] if role is not None else 0
|
return role['id'] if role is not None else 0
|
||||||
@@ -69,8 +71,7 @@ class InitDepartment(object):
|
|||||||
def init(self):
|
def init(self):
|
||||||
self.init_wide_company()
|
self.init_wide_company()
|
||||||
|
|
||||||
@staticmethod
|
def hard_delete(self, department_id, department_name):
|
||||||
def hard_delete(department_id, department_name):
|
|
||||||
existed_deleted_list = Department.query.filter(
|
existed_deleted_list = Department.query.filter(
|
||||||
Department.department_name == department_name,
|
Department.department_name == department_name,
|
||||||
Department.department_id == department_id,
|
Department.department_id == department_id,
|
||||||
@@ -79,12 +80,11 @@ class InitDepartment(object):
|
|||||||
for existed in existed_deleted_list:
|
for existed in existed_deleted_list:
|
||||||
existed.delete()
|
existed.delete()
|
||||||
|
|
||||||
@staticmethod
|
def get_department(self, department_name):
|
||||||
def get_department(department_name):
|
|
||||||
return Department.query.filter(
|
return Department.query.filter(
|
||||||
Department.department_name == department_name,
|
Department.department_name == department_name,
|
||||||
Department.deleted == 0,
|
Department.deleted == 0,
|
||||||
).first()
|
).order_by(Department.created_at.asc()).first()
|
||||||
|
|
||||||
def run(self, department_id, department_name, department_parent_id):
|
def run(self, department_id, department_name, department_parent_id):
|
||||||
self.hard_delete(department_id, department_name)
|
self.hard_delete(department_id, department_name)
|
||||||
@@ -94,7 +94,7 @@ class InitDepartment(object):
|
|||||||
if res.department_id == department_id:
|
if res.department_id == department_id:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
res.update(
|
new_d = res.update(
|
||||||
department_id=department_id,
|
department_id=department_id,
|
||||||
department_parent_id=department_parent_id,
|
department_parent_id=department_parent_id,
|
||||||
)
|
)
|
||||||
@@ -108,11 +108,11 @@ class InitDepartment(object):
|
|||||||
new_d = self.get_department(department_name)
|
new_d = self.get_department(department_name)
|
||||||
|
|
||||||
if new_d.department_id != department_id:
|
if new_d.department_id != department_id:
|
||||||
new_d.update(
|
new_d = new_d.update(
|
||||||
department_id=department_id,
|
department_id=department_id,
|
||||||
department_parent_id=department_parent_id,
|
department_parent_id=department_parent_id,
|
||||||
)
|
)
|
||||||
self.log.info(f"init {department_name} success.")
|
self.log.info(f"初始化 {department_name} 部门成功.")
|
||||||
|
|
||||||
def run_common(self, department_id, department_name, department_parent_id):
|
def run_common(self, department_id, department_name, department_parent_id):
|
||||||
try:
|
try:
|
||||||
@@ -123,14 +123,19 @@ class InitDepartment(object):
|
|||||||
raise Exception(e)
|
raise Exception(e)
|
||||||
|
|
||||||
def init_wide_company(self):
|
def init_wide_company(self):
|
||||||
|
"""
|
||||||
|
创建 id 0, name 全公司 的部门
|
||||||
|
"""
|
||||||
department_id = 0
|
department_id = 0
|
||||||
department_name = '全公司'
|
department_name = '全公司'
|
||||||
department_parent_id = -1
|
department_parent_id = -1
|
||||||
|
|
||||||
self.run_common(department_id, department_name, department_parent_id)
|
self.run_common(department_id, department_name, department_parent_id)
|
||||||
|
|
||||||
@staticmethod
|
def create_acl_role_with_department(self):
|
||||||
def create_acl_role_with_department():
|
"""
|
||||||
|
当前所有部门,在ACL创建 role
|
||||||
|
"""
|
||||||
acl = ACLManager('acl')
|
acl = ACLManager('acl')
|
||||||
role_name_map = {role['name']: role for role in acl.get_all_roles()}
|
role_name_map = {role['name']: role for role in acl.get_all_roles()}
|
||||||
|
|
||||||
@@ -141,7 +146,7 @@ class InitDepartment(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
role = role_name_map.get(department.department_name)
|
role = role_name_map.get(department.department_name)
|
||||||
if not role:
|
if role is None:
|
||||||
payload = {
|
payload = {
|
||||||
'app_id': 'acl',
|
'app_id': 'acl',
|
||||||
'name': department.department_name,
|
'name': department.department_name,
|
||||||
@@ -203,20 +208,25 @@ class InitDepartment(object):
|
|||||||
if acl_rid > 0:
|
if acl_rid > 0:
|
||||||
acl.grant_resource(acl_rid, resource['id'], perms)
|
acl.grant_resource(acl_rid, resource['id'], perms)
|
||||||
|
|
||||||
@staticmethod
|
def check_app(self, app_name):
|
||||||
def check_app(app_name):
|
|
||||||
acl = ACLManager(app_name)
|
acl = ACLManager(app_name)
|
||||||
payload = dict(
|
payload = dict(
|
||||||
name=app_name,
|
name=app_name,
|
||||||
description=app_name
|
description=app_name
|
||||||
)
|
)
|
||||||
app = acl.validate_app()
|
try:
|
||||||
if not app:
|
app = acl.validate_app()
|
||||||
acl.create_app(payload)
|
if not app:
|
||||||
return acl
|
acl.create_app(payload)
|
||||||
|
return acl
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(e)
|
||||||
|
if '不存在' in str(e):
|
||||||
|
acl.create_app(payload)
|
||||||
|
return acl
|
||||||
|
raise Exception(e)
|
||||||
|
|
||||||
@staticmethod
|
def get_admin_user_rid(self):
|
||||||
def get_admin_user_rid():
|
|
||||||
admin = Employee.get_by(first=True, username='admin', to_dict=False)
|
admin = Employee.get_by(first=True, username='admin', to_dict=False)
|
||||||
return admin.acl_rid if admin else 0
|
return admin.acl_rid if admin else 0
|
||||||
|
|
||||||
@@ -251,19 +261,17 @@ def common_check_new_columns():
|
|||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from sqlalchemy import inspect, text
|
from sqlalchemy import inspect, text
|
||||||
|
|
||||||
def get_model_by_table_name(_table_name):
|
def get_model_by_table_name(table_name):
|
||||||
registry = getattr(db.Model, 'registry', None)
|
for model in db.Model.registry._class_registry.values():
|
||||||
class_registry = getattr(registry, '_class_registry', None)
|
if hasattr(model, '__tablename__') and model.__tablename__ == table_name:
|
||||||
for _model in class_registry.values():
|
return model
|
||||||
if hasattr(_model, '__tablename__') and _model.__tablename__ == _table_name:
|
|
||||||
return _model
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add_new_column(target_table_name, new_column):
|
def add_new_column(table_name, new_column):
|
||||||
column_type = new_column.type.compile(engine.dialect)
|
column_type = new_column.type.compile(engine.dialect)
|
||||||
default_value = new_column.default.arg if new_column.default else None
|
default_value = new_column.default.arg if new_column.default else None
|
||||||
|
|
||||||
sql = "ALTER TABLE " + target_table_name + " ADD COLUMN " + new_column.name + " " + column_type
|
sql = f"ALTER TABLE {table_name} ADD COLUMN {new_column.name} {column_type} "
|
||||||
if new_column.comment:
|
if new_column.comment:
|
||||||
sql += f" comment '{new_column.comment}'"
|
sql += f" comment '{new_column.comment}'"
|
||||||
|
|
||||||
@@ -289,8 +297,7 @@ def common_check_new_columns():
|
|||||||
model = get_model_by_table_name(table_name)
|
model = get_model_by_table_name(table_name)
|
||||||
if model is None:
|
if model is None:
|
||||||
continue
|
continue
|
||||||
|
model_columns = model.__table__.columns._all_columns
|
||||||
model_columns = getattr(getattr(getattr(model, '__table__'), 'columns'), '_all_columns')
|
|
||||||
for column in model_columns:
|
for column in model_columns:
|
||||||
if column.name not in existed_column_name_list:
|
if column.name not in existed_column_name_list:
|
||||||
try:
|
try:
|
||||||
@@ -299,20 +306,3 @@ def common_check_new_columns():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f"add new column [{column.name}] in table [{table_name}] err:")
|
current_app.logger.error(f"add new column [{column.name}] in table [{table_name}] err:")
|
||||||
current_app.logger.error(e)
|
current_app.logger.error(e)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
|
||||||
@with_appcontext
|
|
||||||
def common_sync_file_to_db():
|
|
||||||
from api.lib.common_setting.upload_file import CommonFileCRUD
|
|
||||||
CommonFileCRUD.sync_file_to_db()
|
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
|
||||||
@with_appcontext
|
|
||||||
@click.option('--value', type=click.INT, default=-1)
|
|
||||||
def set_auth_auto_redirect_enable(value):
|
|
||||||
if value < 0:
|
|
||||||
return
|
|
||||||
from api.lib.common_setting.common_data import CommonDataCRUD
|
|
||||||
CommonDataCRUD.set_auth_auto_redirect_enable(value)
|
|
||||||
|
@@ -84,6 +84,66 @@ def clean():
|
|||||||
os.remove(full_pathname)
|
os.remove(full_pathname)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option("--url", default=None, help="Url to test (ex. /static/image.png)")
|
||||||
|
@click.option(
|
||||||
|
"--order", default="rule", help="Property on Rule to order by (default: rule)"
|
||||||
|
)
|
||||||
|
@with_appcontext
|
||||||
|
def urls(url, order):
|
||||||
|
"""Display all of the url matching routes for the project.
|
||||||
|
|
||||||
|
Borrowed from Flask-Script, converted to use Click.
|
||||||
|
"""
|
||||||
|
rows = []
|
||||||
|
column_headers = ("Rule", "Endpoint", "Arguments")
|
||||||
|
|
||||||
|
if url:
|
||||||
|
try:
|
||||||
|
rule, arguments = current_app.url_map.bind("localhost").match(
|
||||||
|
url, return_rule=True
|
||||||
|
)
|
||||||
|
rows.append((rule.rule, rule.endpoint, arguments))
|
||||||
|
column_length = 3
|
||||||
|
except (NotFound, MethodNotAllowed) as e:
|
||||||
|
rows.append(("<{}>".format(e), None, None))
|
||||||
|
column_length = 1
|
||||||
|
else:
|
||||||
|
rules = sorted(
|
||||||
|
current_app.url_map.iter_rules(), key=lambda rule: getattr(rule, order)
|
||||||
|
)
|
||||||
|
for rule in rules:
|
||||||
|
rows.append((rule.rule, rule.endpoint, None))
|
||||||
|
column_length = 2
|
||||||
|
|
||||||
|
str_template = ""
|
||||||
|
table_width = 0
|
||||||
|
|
||||||
|
if column_length >= 1:
|
||||||
|
max_rule_length = max(len(r[0]) for r in rows)
|
||||||
|
max_rule_length = max_rule_length if max_rule_length > 4 else 4
|
||||||
|
str_template += "{:" + str(max_rule_length) + "}"
|
||||||
|
table_width += max_rule_length
|
||||||
|
|
||||||
|
if column_length >= 2:
|
||||||
|
max_endpoint_length = max(len(str(r[1])) for r in rows)
|
||||||
|
max_endpoint_length = max_endpoint_length if max_endpoint_length > 8 else 8
|
||||||
|
str_template += " {:" + str(max_endpoint_length) + "}"
|
||||||
|
table_width += 2 + max_endpoint_length
|
||||||
|
|
||||||
|
if column_length >= 3:
|
||||||
|
max_arguments_length = max(len(str(r[2])) for r in rows)
|
||||||
|
max_arguments_length = max_arguments_length if max_arguments_length > 9 else 9
|
||||||
|
str_template += " {:" + str(max_arguments_length) + "}"
|
||||||
|
table_width += 2 + max_arguments_length
|
||||||
|
|
||||||
|
click.echo(str_template.format(*column_headers[:column_length]))
|
||||||
|
click.echo("-" * table_width)
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
click.echo(str_template.format(*row[:column_length]))
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
def db_setup():
|
def db_setup():
|
||||||
|
@@ -15,7 +15,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from flask import _request_ctx_stack as stack
|
from flask import _request_ctx_stack as stack
|
||||||
|
|
||||||
from . import routing
|
from api.flask_cas import routing
|
||||||
|
|
||||||
|
|
||||||
class CAS(object):
|
class CAS(object):
|
@@ -119,4 +119,4 @@ def create_cas_validate_url(cas_url, cas_route, service, ticket,
|
|||||||
('service', service),
|
('service', service),
|
||||||
('ticket', ticket),
|
('ticket', ticket),
|
||||||
('renew', renew),
|
('renew', renew),
|
||||||
)
|
)
|
@@ -1,24 +1,14 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
import datetime
|
|
||||||
import uuid
|
import json
|
||||||
|
|
||||||
import bs4
|
import bs4
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask import current_app
|
from flask import current_app, session, request, url_for, redirect
|
||||||
from flask import redirect
|
from flask_login import login_user, logout_user
|
||||||
from flask import request
|
|
||||||
from flask import session
|
|
||||||
from flask import url_for
|
|
||||||
from flask_login import login_user
|
|
||||||
from flask_login import logout_user
|
|
||||||
from six.moves.urllib.parse import urlparse
|
|
||||||
from six.moves.urllib_request import urlopen
|
from six.moves.urllib_request import urlopen
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.common_setting.const import AuthenticateType
|
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
from api.lib.perm.acl.cache import UserCache
|
from api.lib.perm.acl.cache import UserCache
|
||||||
from api.lib.perm.acl.resp_format import ErrFormat
|
|
||||||
from .cas_urls import create_cas_login_url
|
from .cas_urls import create_cas_login_url
|
||||||
from .cas_urls import create_cas_logout_url
|
from .cas_urls import create_cas_logout_url
|
||||||
from .cas_urls import create_cas_validate_url
|
from .cas_urls import create_cas_validate_url
|
||||||
@@ -26,7 +16,6 @@ from .cas_urls import create_cas_validate_url
|
|||||||
blueprint = Blueprint('cas', __name__)
|
blueprint = Blueprint('cas', __name__)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/cas/login')
|
|
||||||
@blueprint.route('/api/sso/login')
|
@blueprint.route('/api/sso/login')
|
||||||
def login():
|
def login():
|
||||||
"""
|
"""
|
||||||
@@ -40,20 +29,16 @@ def login():
|
|||||||
If validation was successful the logged in username is saved in
|
If validation was successful the logged in username is saved in
|
||||||
the user's session under the key `CAS_USERNAME_SESSION_KEY`.
|
the user's session under the key `CAS_USERNAME_SESSION_KEY`.
|
||||||
"""
|
"""
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.CAS).get()
|
|
||||||
|
|
||||||
cas_token_session_key = current_app.config['CAS_TOKEN_SESSION_KEY']
|
cas_token_session_key = current_app.config['CAS_TOKEN_SESSION_KEY']
|
||||||
if request.values.get("next"):
|
if request.values.get("next"):
|
||||||
session["next"] = request.values.get("next")
|
session["next"] = request.values.get("next")
|
||||||
|
|
||||||
# _service = url_for('cas.login', _external=True)
|
_service = url_for('cas.login', _external=True, next=session["next"]) \
|
||||||
_service = "{}://{}{}".format(urlparse(request.referrer).scheme,
|
if session.get("next") else url_for('cas.login', _external=True)
|
||||||
urlparse(request.referrer).netloc,
|
|
||||||
url_for('cas.login'))
|
|
||||||
|
|
||||||
redirect_url = create_cas_login_url(
|
redirect_url = create_cas_login_url(
|
||||||
config['cas_server'],
|
current_app.config['CAS_SERVER'],
|
||||||
config['cas_login_route'],
|
current_app.config['CAS_LOGIN_ROUTE'],
|
||||||
_service)
|
_service)
|
||||||
|
|
||||||
if 'ticket' in request.args:
|
if 'ticket' in request.args:
|
||||||
@@ -62,38 +47,30 @@ def login():
|
|||||||
if request.args.get('ticket'):
|
if request.args.get('ticket'):
|
||||||
|
|
||||||
if validate(request.args['ticket']):
|
if validate(request.args['ticket']):
|
||||||
redirect_url = session.get("next") or config.get("cas_after_login") or "/"
|
redirect_url = session.get("next") or \
|
||||||
|
current_app.config.get("CAS_AFTER_LOGIN")
|
||||||
username = session.get("CAS_USERNAME")
|
username = session.get("CAS_USERNAME")
|
||||||
user = UserCache.get(username)
|
user = UserCache.get(username)
|
||||||
login_user(user)
|
login_user(user)
|
||||||
|
|
||||||
session.permanent = True
|
session.permanent = True
|
||||||
|
|
||||||
_id = AuditCRUD.add_login_log(username, True, ErrFormat.login_succeed)
|
|
||||||
session['LOGIN_ID'] = _id
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
del session[cas_token_session_key]
|
del session[cas_token_session_key]
|
||||||
redirect_url = create_cas_login_url(
|
redirect_url = create_cas_login_url(
|
||||||
config['cas_server'],
|
current_app.config['CAS_SERVER'],
|
||||||
config['cas_login_route'],
|
current_app.config['CAS_LOGIN_ROUTE'],
|
||||||
url_for('cas.login', _external=True),
|
url_for('cas.login', _external=True),
|
||||||
renew=True)
|
renew=True)
|
||||||
|
|
||||||
AuditCRUD.add_login_log(session.get("CAS_USERNAME"), False, ErrFormat.invalid_password)
|
|
||||||
|
|
||||||
current_app.logger.info("redirect to: {0}".format(redirect_url))
|
current_app.logger.info("redirect to: {0}".format(redirect_url))
|
||||||
return redirect(redirect_url)
|
return redirect(redirect_url)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/cas/logout')
|
|
||||||
@blueprint.route('/api/sso/logout')
|
@blueprint.route('/api/sso/logout')
|
||||||
def logout():
|
def logout():
|
||||||
"""
|
"""
|
||||||
When the user accesses this route they are logged out.
|
When the user accesses this route they are logged out.
|
||||||
"""
|
"""
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.CAS).get()
|
|
||||||
current_app.logger.info(config)
|
|
||||||
|
|
||||||
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
||||||
cas_token_session_key = current_app.config['CAS_TOKEN_SESSION_KEY']
|
cas_token_session_key = current_app.config['CAS_TOKEN_SESSION_KEY']
|
||||||
@@ -105,14 +82,12 @@ def logout():
|
|||||||
"next" in session and session.pop("next")
|
"next" in session and session.pop("next")
|
||||||
|
|
||||||
redirect_url = create_cas_logout_url(
|
redirect_url = create_cas_logout_url(
|
||||||
config['cas_server'],
|
current_app.config['CAS_SERVER'],
|
||||||
config['cas_logout_route'],
|
current_app.config['CAS_LOGOUT_ROUTE'],
|
||||||
url_for('cas.login', _external=True, next=request.referrer))
|
url_for('cas.login', _external=True, next=request.referrer))
|
||||||
|
|
||||||
logout_user()
|
logout_user()
|
||||||
|
|
||||||
AuditCRUD.add_login_log(None, None, None, _id=session.get('LOGIN_ID'), logout_at=datetime.datetime.now())
|
|
||||||
|
|
||||||
current_app.logger.debug('Redirecting to: {0}'.format(redirect_url))
|
current_app.logger.debug('Redirecting to: {0}'.format(redirect_url))
|
||||||
|
|
||||||
return redirect(redirect_url)
|
return redirect(redirect_url)
|
||||||
@@ -125,15 +100,14 @@ def validate(ticket):
|
|||||||
and the validated username is saved in the session under the
|
and the validated username is saved in the session under the
|
||||||
key `CAS_USERNAME_SESSION_KEY`.
|
key `CAS_USERNAME_SESSION_KEY`.
|
||||||
"""
|
"""
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.CAS).get()
|
|
||||||
|
|
||||||
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
||||||
|
|
||||||
current_app.logger.debug("validating token {0}".format(ticket))
|
current_app.logger.debug("validating token {0}".format(ticket))
|
||||||
|
|
||||||
cas_validate_url = create_cas_validate_url(
|
cas_validate_url = create_cas_validate_url(
|
||||||
config['cas_validate_server'],
|
current_app.config['CAS_VALIDATE_SERVER'],
|
||||||
config['cas_validate_route'],
|
current_app.config['CAS_VALIDATE_ROUTE'],
|
||||||
url_for('cas.login', _external=True),
|
url_for('cas.login', _external=True),
|
||||||
ticket)
|
ticket)
|
||||||
|
|
||||||
@@ -141,35 +115,23 @@ def validate(ticket):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
response = urlopen(cas_validate_url).read()
|
response = urlopen(cas_validate_url).read()
|
||||||
ticket_id = _parse_tag(response, "cas:user")
|
ticketid = _parse_tag(response, "cas:user")
|
||||||
strs = [s.strip() for s in ticket_id.split('|') if s.strip()]
|
strs = [s.strip() for s in ticketid.split('|') if s.strip()]
|
||||||
username, is_valid = None, False
|
username, is_valid = None, False
|
||||||
if len(strs) == 1:
|
if len(strs) == 1:
|
||||||
username = strs[0]
|
username = strs[0]
|
||||||
is_valid = True
|
is_valid = True
|
||||||
|
user_info = json.loads(_parse_tag(response, "cas:other"))
|
||||||
|
current_app.logger.info(user_info)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
current_app.logger.error("CAS returned unexpected result")
|
current_app.logger.error("CAS returned unexpected result")
|
||||||
is_valid = False
|
is_valid = False
|
||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
if is_valid:
|
if is_valid:
|
||||||
current_app.logger.debug("{}: {}".format(cas_username_session_key, username))
|
current_app.logger.debug("valid")
|
||||||
session[cas_username_session_key] = username
|
session[cas_username_session_key] = username
|
||||||
user = UserCache.get(username)
|
user = UserCache.get(username)
|
||||||
if user is None:
|
|
||||||
current_app.logger.info("create user: {}".format(username))
|
|
||||||
from api.lib.perm.acl.user import UserCRUD
|
|
||||||
soup = bs4.BeautifulSoup(response)
|
|
||||||
cas_user_map = config.get('cas_user_map')
|
|
||||||
user_dict = dict()
|
|
||||||
for k in cas_user_map:
|
|
||||||
v = soup.find(cas_user_map[k]['tag'], cas_user_map[k].get('attrs', {}))
|
|
||||||
user_dict[k] = v and v.text or None
|
|
||||||
user_dict['password'] = uuid.uuid4().hex
|
|
||||||
if "email" not in user_dict:
|
|
||||||
user_dict['email'] = username
|
|
||||||
|
|
||||||
UserCRUD.add(**user_dict)
|
|
||||||
|
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
from api.lib.perm.acl.acl import ACLManager
|
||||||
user_info = ACLManager.get_user_info(username)
|
user_info = ACLManager.get_user_info(username)
|
||||||
@@ -202,5 +164,4 @@ def _parse_tag(string, tag):
|
|||||||
|
|
||||||
if soup.find(tag) is None:
|
if soup.find(tag) is None:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
return soup.find(tag).string.strip()
|
return soup.find(tag).string.strip()
|
@@ -81,9 +81,8 @@ class AttributeManager(object):
|
|||||||
elif choice_other.get('script'):
|
elif choice_other.get('script'):
|
||||||
try:
|
try:
|
||||||
x = compile(choice_other['script'], '', "exec")
|
x = compile(choice_other['script'], '', "exec")
|
||||||
local_ns = {}
|
exec(x)
|
||||||
exec(x, {}, local_ns)
|
res = locals()['ChoiceValue']().values() or []
|
||||||
res = local_ns['ChoiceValue']().values() or []
|
|
||||||
return [[i, {}] for i in res]
|
return [[i, {}] for i in res]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error("get choice values from script: {}".format(e))
|
current_app.logger.error("get choice values from script: {}".format(e))
|
||||||
@@ -337,6 +336,9 @@ class AttributeManager(object):
|
|||||||
def update(self, _id, **kwargs):
|
def update(self, _id, **kwargs):
|
||||||
attr = Attribute.get_by_id(_id) or abort(404, ErrFormat.attribute_not_found.format("id={}".format(_id)))
|
attr = Attribute.get_by_id(_id) or abort(404, ErrFormat.attribute_not_found.format("id={}".format(_id)))
|
||||||
|
|
||||||
|
if not self._can_edit_attribute(attr):
|
||||||
|
return abort(403, ErrFormat.cannot_edit_attribute)
|
||||||
|
|
||||||
if kwargs.get("name"):
|
if kwargs.get("name"):
|
||||||
other = Attribute.get_by(name=kwargs['name'], first=True, to_dict=False)
|
other = Attribute.get_by(name=kwargs['name'], first=True, to_dict=False)
|
||||||
if other and other.id != attr.id:
|
if other and other.id != attr.id:
|
||||||
@@ -377,14 +379,6 @@ class AttributeManager(object):
|
|||||||
|
|
||||||
kwargs.get('is_computed') and self.can_create_computed_attribute()
|
kwargs.get('is_computed') and self.can_create_computed_attribute()
|
||||||
|
|
||||||
is_changed = False
|
|
||||||
for k in kwargs:
|
|
||||||
if kwargs[k] != getattr(attr, k, None):
|
|
||||||
is_changed = True
|
|
||||||
|
|
||||||
if is_changed and not self._can_edit_attribute(attr):
|
|
||||||
return abort(403, ErrFormat.cannot_edit_attribute)
|
|
||||||
|
|
||||||
attr.update(flush=True, filter_none=False, **kwargs)
|
attr.update(flush=True, filter_none=False, **kwargs)
|
||||||
|
|
||||||
if is_choice and choice_value:
|
if is_choice and choice_value:
|
||||||
|
@@ -3,6 +3,11 @@ import datetime
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from flask import abort
|
||||||
|
from flask import current_app
|
||||||
|
from flask_login import current_user
|
||||||
|
from sqlalchemy import func
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.cmdb.auto_discovery.const import ClOUD_MAP
|
from api.lib.cmdb.auto_discovery.const import ClOUD_MAP
|
||||||
from api.lib.cmdb.cache import CITypeAttributeCache
|
from api.lib.cmdb.cache import CITypeAttributeCache
|
||||||
@@ -23,10 +28,6 @@ from api.lib.utils import AESCrypto
|
|||||||
from api.models.cmdb import AutoDiscoveryCI
|
from api.models.cmdb import AutoDiscoveryCI
|
||||||
from api.models.cmdb import AutoDiscoveryCIType
|
from api.models.cmdb import AutoDiscoveryCIType
|
||||||
from api.models.cmdb import AutoDiscoveryRule
|
from api.models.cmdb import AutoDiscoveryRule
|
||||||
from flask import abort
|
|
||||||
from flask import current_app
|
|
||||||
from flask_login import current_user
|
|
||||||
from sqlalchemy import func
|
|
||||||
|
|
||||||
PWD = os.path.abspath(os.path.dirname(__file__))
|
PWD = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
@@ -35,10 +36,9 @@ def parse_plugin_script(script):
|
|||||||
attributes = []
|
attributes = []
|
||||||
try:
|
try:
|
||||||
x = compile(script, '', "exec")
|
x = compile(script, '', "exec")
|
||||||
local_ns = {}
|
exec(x)
|
||||||
exec(x, {}, local_ns)
|
unique_key = locals()['AutoDiscovery']().unique_key
|
||||||
unique_key = local_ns['AutoDiscovery']().unique_key
|
attrs = locals()['AutoDiscovery']().attributes() or []
|
||||||
attrs = local_ns['AutoDiscovery']().attributes() or []
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return abort(400, str(e))
|
return abort(400, str(e))
|
||||||
|
|
||||||
@@ -250,17 +250,20 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
|||||||
current_app.logger.warning(e)
|
current_app.logger.warning(e)
|
||||||
return abort(400, str(e))
|
return abort(400, str(e))
|
||||||
|
|
||||||
@staticmethod
|
def _can_add(self, **kwargs):
|
||||||
def _can_add(**kwargs):
|
self.cls.get_by(type_id=kwargs['type_id'], adr_id=kwargs.get('adr_id') or None) and abort(
|
||||||
|
400, ErrFormat.ad_duplicate)
|
||||||
|
|
||||||
|
# self.__valid_exec_target(kwargs.get('agent_id'), kwargs.get('query_expr'))
|
||||||
|
|
||||||
if kwargs.get('adr_id'):
|
if kwargs.get('adr_id'):
|
||||||
AutoDiscoveryRule.get_by_id(kwargs['adr_id']) or abort(
|
adr = AutoDiscoveryRule.get_by_id(kwargs['adr_id']) or abort(
|
||||||
404, ErrFormat.adr_not_found.format("id={}".format(kwargs['adr_id'])))
|
404, ErrFormat.adr_not_found.format("id={}".format(kwargs['adr_id'])))
|
||||||
# if not adr.is_plugin:
|
if not adr.is_plugin:
|
||||||
# other = self.cls.get_by(adr_id=adr.id, first=True, to_dict=False)
|
other = self.cls.get_by(adr_id=adr.id, first=True, to_dict=False)
|
||||||
# if other:
|
if other:
|
||||||
# ci_type = CITypeCache.get(other.type_id)
|
ci_type = CITypeCache.get(other.type_id)
|
||||||
# return abort(400, ErrFormat.adr_default_ref_once.format(ci_type.alias))
|
return abort(400, ErrFormat.adr_default_ref_once.format(ci_type.alias))
|
||||||
|
|
||||||
if kwargs.get('is_plugin') and kwargs.get('plugin_script'):
|
if kwargs.get('is_plugin') and kwargs.get('plugin_script'):
|
||||||
kwargs = check_plugin_script(**kwargs)
|
kwargs = check_plugin_script(**kwargs)
|
||||||
|
@@ -45,8 +45,8 @@ from api.lib.perm.acl.acl import is_app_admin
|
|||||||
from api.lib.perm.acl.acl import validate_permission
|
from api.lib.perm.acl.acl import validate_permission
|
||||||
from api.lib.secrets.inner import InnerCrypt
|
from api.lib.secrets.inner import InnerCrypt
|
||||||
from api.lib.secrets.vault import VaultClient
|
from api.lib.secrets.vault import VaultClient
|
||||||
from api.lib.utils import Lock
|
|
||||||
from api.lib.utils import handle_arg_list
|
from api.lib.utils import handle_arg_list
|
||||||
|
from api.lib.utils import Lock
|
||||||
from api.lib.webhook import webhook_request
|
from api.lib.webhook import webhook_request
|
||||||
from api.models.cmdb import AttributeHistory
|
from api.models.cmdb import AttributeHistory
|
||||||
from api.models.cmdb import AutoDiscoveryCI
|
from api.models.cmdb import AutoDiscoveryCI
|
||||||
@@ -182,9 +182,6 @@ class CIManager(object):
|
|||||||
need_children and res.update(CIRelationManager.get_children(ci_id, ret_key=ret_key)) # one floor
|
need_children and res.update(CIRelationManager.get_children(ci_id, ret_key=ret_key)) # one floor
|
||||||
|
|
||||||
ci_type = CITypeCache.get(ci.type_id)
|
ci_type = CITypeCache.get(ci.type_id)
|
||||||
if not ci_type:
|
|
||||||
return res
|
|
||||||
|
|
||||||
res["ci_type"] = ci_type.name
|
res["ci_type"] = ci_type.name
|
||||||
|
|
||||||
fields = CITypeAttributeManager.get_attr_names_by_type_id(ci.type_id) if not fields else fields
|
fields = CITypeAttributeManager.get_attr_names_by_type_id(ci.type_id) if not fields else fields
|
||||||
@@ -514,20 +511,18 @@ class CIManager(object):
|
|||||||
ci_delete_trigger.apply_async(args=(trigger, OperateType.DELETE, ci_dict), queue=CMDB_QUEUE)
|
ci_delete_trigger.apply_async(args=(trigger, OperateType.DELETE, ci_dict), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
attrs = CITypeAttribute.get_by(type_id=ci.type_id, to_dict=False)
|
attrs = CITypeAttribute.get_by(type_id=ci.type_id, to_dict=False)
|
||||||
attrs = [AttributeCache.get(attr.attr_id) for attr in attrs]
|
attr_names = set([AttributeCache.get(attr.attr_id).name for attr in attrs])
|
||||||
for attr in attrs:
|
for attr_name in attr_names:
|
||||||
value_table = TableMap(attr=attr).table
|
value_table = TableMap(attr_name=attr_name).table
|
||||||
for item in value_table.get_by(ci_id=ci_id, to_dict=False):
|
for item in value_table.get_by(ci_id=ci_id, to_dict=False):
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
for item in CIRelation.get_by(first_ci_id=ci_id, to_dict=False):
|
for item in CIRelation.get_by(first_ci_id=ci_id, to_dict=False):
|
||||||
ci_relation_delete.apply_async(
|
ci_relation_delete.apply_async(args=(item.first_ci_id, item.second_ci_id), queue=CMDB_QUEUE)
|
||||||
args=(item.first_ci_id, item.second_ci_id, item.ancestor_ids), queue=CMDB_QUEUE)
|
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
for item in CIRelation.get_by(second_ci_id=ci_id, to_dict=False):
|
for item in CIRelation.get_by(second_ci_id=ci_id, to_dict=False):
|
||||||
ci_relation_delete.apply_async(
|
ci_relation_delete.apply_async(args=(item.first_ci_id, item.second_ci_id), queue=CMDB_QUEUE)
|
||||||
args=(item.first_ci_id, item.second_ci_id, item.ancestor_ids), queue=CMDB_QUEUE)
|
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
ad_ci = AutoDiscoveryCI.get_by(ci_id=ci_id, to_dict=False, first=True)
|
ad_ci = AutoDiscoveryCI.get_by(ci_id=ci_id, to_dict=False, first=True)
|
||||||
@@ -644,9 +639,6 @@ class CIManager(object):
|
|||||||
_fields.append(str(attr.id))
|
_fields.append(str(attr.id))
|
||||||
filter_fields_sql = "WHERE A.attr_id in ({0})".format(",".join(_fields))
|
filter_fields_sql = "WHERE A.attr_id in ({0})".format(",".join(_fields))
|
||||||
|
|
||||||
ci2pos = {int(_id): _pos for _pos, _id in enumerate(ci_ids)}
|
|
||||||
res = [None] * len(ci_ids)
|
|
||||||
|
|
||||||
ci_ids = ",".join(map(str, ci_ids))
|
ci_ids = ",".join(map(str, ci_ids))
|
||||||
if value_tables is None:
|
if value_tables is None:
|
||||||
value_tables = ValueTypeMap.table_name.values()
|
value_tables = ValueTypeMap.table_name.values()
|
||||||
@@ -657,6 +649,7 @@ class CIManager(object):
|
|||||||
# current_app.logger.debug(query_sql)
|
# current_app.logger.debug(query_sql)
|
||||||
cis = db.session.execute(query_sql).fetchall()
|
cis = db.session.execute(query_sql).fetchall()
|
||||||
ci_set = set()
|
ci_set = set()
|
||||||
|
res = list()
|
||||||
ci_dict = dict()
|
ci_dict = dict()
|
||||||
unique_id2obj = dict()
|
unique_id2obj = dict()
|
||||||
excludes = excludes and set(excludes)
|
excludes = excludes and set(excludes)
|
||||||
@@ -676,7 +669,7 @@ class CIManager(object):
|
|||||||
ci_dict["unique"] = unique_id2obj[ci_type.unique_id] and unique_id2obj[ci_type.unique_id].name
|
ci_dict["unique"] = unique_id2obj[ci_type.unique_id] and unique_id2obj[ci_type.unique_id].name
|
||||||
ci_dict["unique_alias"] = unique_id2obj[ci_type.unique_id] and unique_id2obj[ci_type.unique_id].alias
|
ci_dict["unique_alias"] = unique_id2obj[ci_type.unique_id] and unique_id2obj[ci_type.unique_id].alias
|
||||||
ci_set.add(ci_id)
|
ci_set.add(ci_id)
|
||||||
res[ci2pos[ci_id]] = ci_dict
|
res.append(ci_dict)
|
||||||
|
|
||||||
if ret_key == RetKey.NAME:
|
if ret_key == RetKey.NAME:
|
||||||
attr_key = attr_name
|
attr_key = attr_name
|
||||||
@@ -891,14 +884,12 @@ class CIRelationManager(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ancestor_ids(cls, ci_ids, level=1):
|
def get_ancestor_ids(cls, ci_ids, level=1):
|
||||||
level2ids = dict()
|
for _ in range(level):
|
||||||
for _level in range(1, level + 1):
|
cis = db.session.query(CIRelation.first_ci_id).filter(
|
||||||
cis = db.session.query(CIRelation.first_ci_id, CIRelation.ancestor_ids).filter(
|
|
||||||
CIRelation.second_ci_id.in_(ci_ids)).filter(CIRelation.deleted.is_(False))
|
CIRelation.second_ci_id.in_(ci_ids)).filter(CIRelation.deleted.is_(False))
|
||||||
ci_ids = [i.first_ci_id for i in cis]
|
ci_ids = [i.first_ci_id for i in cis]
|
||||||
level2ids[_level + 1] = {int(i.ancestor_ids.split(',')[-1]) for i in cis if i.ancestor_ids}
|
|
||||||
|
|
||||||
return ci_ids, level2ids
|
return ci_ids
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_constraint(first_ci_id, first_type_id, second_ci_id, second_type_id, type_relation):
|
def _check_constraint(first_ci_id, first_type_id, second_ci_id, second_type_id, type_relation):
|
||||||
@@ -925,14 +916,13 @@ class CIRelationManager(object):
|
|||||||
return abort(400, ErrFormat.relation_constraint.format("1-N"))
|
return abort(400, ErrFormat.relation_constraint.format("1-N"))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None, ancestor_ids=None):
|
def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None):
|
||||||
|
|
||||||
first_ci = CIManager.confirm_ci_existed(first_ci_id)
|
first_ci = CIManager.confirm_ci_existed(first_ci_id)
|
||||||
second_ci = CIManager.confirm_ci_existed(second_ci_id)
|
second_ci = CIManager.confirm_ci_existed(second_ci_id)
|
||||||
|
|
||||||
existed = CIRelation.get_by(first_ci_id=first_ci_id,
|
existed = CIRelation.get_by(first_ci_id=first_ci_id,
|
||||||
second_ci_id=second_ci_id,
|
second_ci_id=second_ci_id,
|
||||||
ancestor_ids=ancestor_ids,
|
|
||||||
to_dict=False,
|
to_dict=False,
|
||||||
first=True)
|
first=True)
|
||||||
if existed is not None:
|
if existed is not None:
|
||||||
@@ -968,12 +958,11 @@ class CIRelationManager(object):
|
|||||||
|
|
||||||
existed = CIRelation.create(first_ci_id=first_ci_id,
|
existed = CIRelation.create(first_ci_id=first_ci_id,
|
||||||
second_ci_id=second_ci_id,
|
second_ci_id=second_ci_id,
|
||||||
relation_type_id=relation_type_id,
|
relation_type_id=relation_type_id)
|
||||||
ancestor_ids=ancestor_ids)
|
|
||||||
|
|
||||||
CIRelationHistoryManager().add(existed, OperateType.ADD)
|
CIRelationHistoryManager().add(existed, OperateType.ADD)
|
||||||
|
|
||||||
ci_relation_cache.apply_async(args=(first_ci_id, second_ci_id, ancestor_ids), queue=CMDB_QUEUE)
|
ci_relation_cache.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
if more is not None:
|
if more is not None:
|
||||||
existed.upadte(more=more)
|
existed.upadte(more=more)
|
||||||
@@ -997,56 +986,53 @@ class CIRelationManager(object):
|
|||||||
his_manager = CIRelationHistoryManager()
|
his_manager = CIRelationHistoryManager()
|
||||||
his_manager.add(cr, operate_type=OperateType.DELETE)
|
his_manager.add(cr, operate_type=OperateType.DELETE)
|
||||||
|
|
||||||
ci_relation_delete.apply_async(args=(cr.first_ci_id, cr.second_ci_id, cr.ancestor_ids), queue=CMDB_QUEUE)
|
ci_relation_delete.apply_async(args=(cr.first_ci_id, cr.second_ci_id), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
return cr_id
|
return cr_id
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete_2(cls, first_ci_id, second_ci_id, ancestor_ids=None):
|
def delete_2(cls, first_ci_id, second_ci_id):
|
||||||
cr = CIRelation.get_by(first_ci_id=first_ci_id,
|
cr = CIRelation.get_by(first_ci_id=first_ci_id,
|
||||||
second_ci_id=second_ci_id,
|
second_ci_id=second_ci_id,
|
||||||
ancestor_ids=ancestor_ids,
|
|
||||||
to_dict=False,
|
to_dict=False,
|
||||||
first=True)
|
first=True)
|
||||||
|
|
||||||
ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id, ancestor_ids), queue=CMDB_QUEUE)
|
ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
return cr and cls.delete(cr.id)
|
return cls.delete(cr.id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def batch_update(cls, ci_ids, parents, children, ancestor_ids=None):
|
def batch_update(cls, ci_ids, parents, children):
|
||||||
"""
|
"""
|
||||||
only for many to one
|
only for many to one
|
||||||
:param ci_ids:
|
:param ci_ids:
|
||||||
:param parents:
|
:param parents:
|
||||||
:param children:
|
:param children:
|
||||||
:param ancestor_ids:
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if isinstance(parents, list):
|
if isinstance(parents, list):
|
||||||
for parent_id in parents:
|
for parent_id in parents:
|
||||||
for ci_id in ci_ids:
|
for ci_id in ci_ids:
|
||||||
cls.add(parent_id, ci_id, ancestor_ids=ancestor_ids)
|
cls.add(parent_id, ci_id)
|
||||||
|
|
||||||
if isinstance(children, list):
|
if isinstance(children, list):
|
||||||
for child_id in children:
|
for child_id in children:
|
||||||
for ci_id in ci_ids:
|
for ci_id in ci_ids:
|
||||||
cls.add(ci_id, child_id, ancestor_ids=ancestor_ids)
|
cls.add(ci_id, child_id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def batch_delete(cls, ci_ids, parents, ancestor_ids=None):
|
def batch_delete(cls, ci_ids, parents):
|
||||||
"""
|
"""
|
||||||
only for many to one
|
only for many to one
|
||||||
:param ci_ids:
|
:param ci_ids:
|
||||||
:param parents:
|
:param parents:
|
||||||
:param ancestor_ids:
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(parents, list):
|
if isinstance(parents, list):
|
||||||
for parent_id in parents:
|
for parent_id in parents:
|
||||||
for ci_id in ci_ids:
|
for ci_id in ci_ids:
|
||||||
cls.delete_2(parent_id, ci_id, ancestor_ids=ancestor_ids)
|
cls.delete_2(parent_id, ci_id)
|
||||||
|
|
||||||
|
|
||||||
class CITriggerManager(object):
|
class CITriggerManager(object):
|
||||||
|
@@ -637,16 +637,6 @@ class CITypeRelationManager(object):
|
|||||||
current_app.logger.warning(str(e))
|
current_app.logger.warning(str(e))
|
||||||
return abort(400, ErrFormat.circular_dependency_error)
|
return abort(400, ErrFormat.circular_dependency_error)
|
||||||
|
|
||||||
if constraint == ConstraintEnum.Many2Many:
|
|
||||||
other_c = CITypeRelation.get_by(parent_id=p.id, constraint=ConstraintEnum.Many2Many,
|
|
||||||
to_dict=False, first=True)
|
|
||||||
other_p = CITypeRelation.get_by(child_id=c.id, constraint=ConstraintEnum.Many2Many,
|
|
||||||
to_dict=False, first=True)
|
|
||||||
if other_c and other_c.child_id != c.id:
|
|
||||||
return abort(400, ErrFormat.m2m_relation_constraint.format(p.name, other_c.child.name))
|
|
||||||
if other_p and other_p.parent_id != p.id:
|
|
||||||
return abort(400, ErrFormat.m2m_relation_constraint.format(other_p.parent.name, c.name))
|
|
||||||
|
|
||||||
existed = cls._get(p.id, c.id)
|
existed = cls._get(p.id, c.id)
|
||||||
if existed is not None:
|
if existed is not None:
|
||||||
existed.update(relation_type_id=relation_type_id,
|
existed.update(relation_type_id=relation_type_id,
|
||||||
@@ -696,24 +686,6 @@ class CITypeRelationManager(object):
|
|||||||
|
|
||||||
cls.delete(ctr.id)
|
cls.delete(ctr.id)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_level2constraint(root_id, level):
|
|
||||||
level = level + 1 if level == 1 else level
|
|
||||||
ci = CI.get_by_id(root_id)
|
|
||||||
if ci is None:
|
|
||||||
return dict()
|
|
||||||
|
|
||||||
root_id = ci.type_id
|
|
||||||
level2constraint = dict()
|
|
||||||
for lv in range(1, int(level) + 1):
|
|
||||||
for i in CITypeRelation.get_by(parent_id=root_id, to_dict=False):
|
|
||||||
if i.constraint == ConstraintEnum.Many2Many:
|
|
||||||
root_id = i.child_id
|
|
||||||
level2constraint[lv] = ConstraintEnum.Many2Many
|
|
||||||
break
|
|
||||||
|
|
||||||
return level2constraint
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeAttributeGroupManager(object):
|
class CITypeAttributeGroupManager(object):
|
||||||
cls = CITypeAttributeGroup
|
cls = CITypeAttributeGroup
|
||||||
|
@@ -100,7 +100,6 @@ class AttributeDefaultValueEnum(BaseEnum):
|
|||||||
CMDB_QUEUE = "one_cmdb_async"
|
CMDB_QUEUE = "one_cmdb_async"
|
||||||
REDIS_PREFIX_CI = "ONE_CMDB"
|
REDIS_PREFIX_CI = "ONE_CMDB"
|
||||||
REDIS_PREFIX_CI_RELATION = "CMDB_CI_RELATION"
|
REDIS_PREFIX_CI_RELATION = "CMDB_CI_RELATION"
|
||||||
REDIS_PREFIX_CI_RELATION2 = "CMDB_CI_RELATION2"
|
|
||||||
|
|
||||||
BUILTIN_KEYWORDS = {'id', '_id', 'ci_id', 'type', '_type', 'ci_type'}
|
BUILTIN_KEYWORDS = {'id', '_id', 'ci_id', 'type', '_type', 'ci_type'}
|
||||||
|
|
||||||
|
@@ -135,7 +135,7 @@ class AttributeHistoryManger(object):
|
|||||||
from api.lib.cmdb.ci import CIManager
|
from api.lib.cmdb.ci import CIManager
|
||||||
cis = CIManager().get_cis_by_ids(list(ci_ids),
|
cis = CIManager().get_cis_by_ids(list(ci_ids),
|
||||||
unique_required=True)
|
unique_required=True)
|
||||||
cis = {i['_id']: i for i in cis if i}
|
cis = {i['_id']: i for i in cis}
|
||||||
|
|
||||||
return total, res, cis
|
return total, res, cis
|
||||||
|
|
||||||
|
@@ -14,10 +14,7 @@ from api.lib.cmdb.attribute import AttributeManager
|
|||||||
from api.lib.cmdb.cache import AttributeCache
|
from api.lib.cmdb.cache import AttributeCache
|
||||||
from api.lib.cmdb.cache import CITypeAttributesCache
|
from api.lib.cmdb.cache import CITypeAttributesCache
|
||||||
from api.lib.cmdb.cache import CITypeCache
|
from api.lib.cmdb.cache import CITypeCache
|
||||||
from api.lib.cmdb.const import ConstraintEnum
|
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum
|
||||||
from api.lib.cmdb.const import PermEnum
|
|
||||||
from api.lib.cmdb.const import ResourceTypeEnum
|
|
||||||
from api.lib.cmdb.const import RoleEnum
|
|
||||||
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
||||||
from api.lib.cmdb.resp_format import ErrFormat
|
from api.lib.cmdb.resp_format import ErrFormat
|
||||||
from api.lib.exception import AbortException
|
from api.lib.exception import AbortException
|
||||||
@@ -232,28 +229,14 @@ class PreferenceManager(object):
|
|||||||
if not parents:
|
if not parents:
|
||||||
return
|
return
|
||||||
|
|
||||||
for _l in leaf:
|
for l in leaf:
|
||||||
_find_parent(_l)
|
_find_parent(l)
|
||||||
|
|
||||||
for node_id in node2show_types:
|
for node_id in node2show_types:
|
||||||
node2show_types[node_id] = [CITypeCache.get(i).to_dict() for i in set(node2show_types[node_id])]
|
node2show_types[node_id] = [CITypeCache.get(i).to_dict() for i in set(node2show_types[node_id])]
|
||||||
|
|
||||||
topo_flatten = list(toposort.toposort_flatten(topo))
|
|
||||||
level2constraint = {}
|
|
||||||
for i, _ in enumerate(topo_flatten[1:]):
|
|
||||||
ctr = CITypeRelation.get_by(
|
|
||||||
parent_id=topo_flatten[i], child_id=topo_flatten[i + 1], first=True, to_dict=False)
|
|
||||||
level2constraint[i + 1] = ctr and ctr.constraint
|
|
||||||
|
|
||||||
if leaf2show_types.get(topo_flatten[-1]):
|
|
||||||
ctr = CITypeRelation.get_by(
|
|
||||||
parent_id=topo_flatten[-1],
|
|
||||||
child_id=leaf2show_types[topo_flatten[-1]][0], first=True, to_dict=False)
|
|
||||||
level2constraint[len(topo_flatten)] = ctr and ctr.constraint
|
|
||||||
|
|
||||||
result[view_name] = dict(topo=list(map(list, toposort.toposort(topo))),
|
result[view_name] = dict(topo=list(map(list, toposort.toposort(topo))),
|
||||||
topo_flatten=topo_flatten,
|
topo_flatten=list(toposort.toposort_flatten(topo)),
|
||||||
level2constraint=level2constraint,
|
|
||||||
leaf=leaf,
|
leaf=leaf,
|
||||||
leaf2show_types=leaf2show_types,
|
leaf2show_types=leaf2show_types,
|
||||||
node2show_types=node2show_types,
|
node2show_types=node2show_types,
|
||||||
@@ -355,29 +338,3 @@ class PreferenceManager(object):
|
|||||||
|
|
||||||
for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False):
|
for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False):
|
||||||
i.soft_delete()
|
i.soft_delete()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def can_edit_relation(parent_id, child_id):
|
|
||||||
views = PreferenceRelationView.get_by(to_dict=False)
|
|
||||||
for view in views:
|
|
||||||
has_m2m = False
|
|
||||||
last_node_id = None
|
|
||||||
for cr in view.cr_ids:
|
|
||||||
_rel = CITypeRelation.get_by(parent_id=cr['parent_id'], child_id=cr['child_id'],
|
|
||||||
first=True, to_dict=False)
|
|
||||||
if _rel and _rel.constraint == ConstraintEnum.Many2Many:
|
|
||||||
has_m2m = True
|
|
||||||
|
|
||||||
if parent_id == _rel.parent_id and child_id == _rel.child_id:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if _rel:
|
|
||||||
last_node_id = _rel.child_id
|
|
||||||
|
|
||||||
if parent_id == last_node_id:
|
|
||||||
rels = CITypeRelation.get_by(parent_id=last_node_id, to_dict=False)
|
|
||||||
for rel in rels:
|
|
||||||
if rel.child_id == child_id and has_m2m:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
@@ -31,7 +31,6 @@ class ErrFormat(CommonErrFormat):
|
|||||||
unique_key_required = "主键字段 {} 缺失"
|
unique_key_required = "主键字段 {} 缺失"
|
||||||
ci_is_already_existed = "CI 已经存在!"
|
ci_is_already_existed = "CI 已经存在!"
|
||||||
relation_constraint = "关系约束: {}, 校验失败 "
|
relation_constraint = "关系约束: {}, 校验失败 "
|
||||||
m2m_relation_constraint = "多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系!"
|
|
||||||
relation_not_found = "CI关系: {} 不存在"
|
relation_not_found = "CI关系: {} 不存在"
|
||||||
ci_search_Parentheses_invalid = "搜索表达式里小括号前不支持: 或、非"
|
ci_search_Parentheses_invalid = "搜索表达式里小括号前不支持: 或、非"
|
||||||
|
|
||||||
|
@@ -9,7 +9,6 @@ import time
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from sqlalchemy import text
|
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.cmdb.cache import AttributeCache
|
from api.lib.cmdb.cache import AttributeCache
|
||||||
@@ -313,7 +312,7 @@ class Search(object):
|
|||||||
start = time.time()
|
start = time.time()
|
||||||
execute = db.session.execute
|
execute = db.session.execute
|
||||||
# current_app.logger.debug(v_query_sql)
|
# current_app.logger.debug(v_query_sql)
|
||||||
res = execute(text(v_query_sql)).fetchall()
|
res = execute(v_query_sql).fetchall()
|
||||||
end_time = time.time()
|
end_time = time.time()
|
||||||
current_app.logger.debug("query ci ids time is: {0}".format(end_time - start))
|
current_app.logger.debug("query ci ids time is: {0}".format(end_time - start))
|
||||||
|
|
||||||
@@ -526,7 +525,7 @@ class Search(object):
|
|||||||
if k:
|
if k:
|
||||||
table_name = TableMap(attr=attr).table_name
|
table_name = TableMap(attr=attr).table_name
|
||||||
query_sql = FACET_QUERY.format(table_name, self.query_sql, attr.id)
|
query_sql = FACET_QUERY.format(table_name, self.query_sql, attr.id)
|
||||||
result = db.session.execute(text(query_sql)).fetchall()
|
result = db.session.execute(query_sql).fetchall()
|
||||||
facet[k] = result
|
facet[k] = result
|
||||||
|
|
||||||
facet_result = dict()
|
facet_result = dict()
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
@@ -8,14 +10,11 @@ from flask import current_app
|
|||||||
from api.extensions import rd
|
from api.extensions import rd
|
||||||
from api.lib.cmdb.ci import CIRelationManager
|
from api.lib.cmdb.ci import CIRelationManager
|
||||||
from api.lib.cmdb.ci_type import CITypeRelationManager
|
from api.lib.cmdb.ci_type import CITypeRelationManager
|
||||||
from api.lib.cmdb.const import ConstraintEnum
|
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
|
|
||||||
from api.lib.cmdb.resp_format import ErrFormat
|
from api.lib.cmdb.resp_format import ErrFormat
|
||||||
from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB
|
from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB
|
||||||
from api.lib.cmdb.search.ci.es.search import Search as SearchFromES
|
from api.lib.cmdb.search.ci.es.search import Search as SearchFromES
|
||||||
from api.models.cmdb import CI
|
from api.models.cmdb import CI
|
||||||
from api.models.cmdb import CIRelation
|
|
||||||
|
|
||||||
|
|
||||||
class Search(object):
|
class Search(object):
|
||||||
@@ -27,8 +26,7 @@ class Search(object):
|
|||||||
page=1,
|
page=1,
|
||||||
count=None,
|
count=None,
|
||||||
sort=None,
|
sort=None,
|
||||||
reverse=False,
|
reverse=False):
|
||||||
ancestor_ids=None):
|
|
||||||
self.orig_query = query
|
self.orig_query = query
|
||||||
self.fl = fl
|
self.fl = fl
|
||||||
self.facet_field = facet_field
|
self.facet_field = facet_field
|
||||||
@@ -40,81 +38,25 @@ class Search(object):
|
|||||||
self.level = level or 0
|
self.level = level or 0
|
||||||
self.reverse = reverse
|
self.reverse = reverse
|
||||||
|
|
||||||
self.level2constraint = CITypeRelationManager.get_level2constraint(
|
def _get_ids(self):
|
||||||
root_id[0] if root_id and isinstance(root_id, list) else root_id,
|
|
||||||
level[0] if isinstance(level, list) and level else level)
|
|
||||||
|
|
||||||
self.ancestor_ids = ancestor_ids
|
|
||||||
self.has_m2m = False
|
|
||||||
if self.ancestor_ids:
|
|
||||||
self.has_m2m = True
|
|
||||||
else:
|
|
||||||
level = level[0] if isinstance(level, list) and level else level
|
|
||||||
for _l, c in self.level2constraint.items():
|
|
||||||
if _l < int(level) and c == ConstraintEnum.Many2Many:
|
|
||||||
self.has_m2m = True
|
|
||||||
|
|
||||||
def _get_ids(self, ids):
|
|
||||||
if self.level[-1] == 1 and len(ids) == 1:
|
|
||||||
if self.ancestor_ids is None:
|
|
||||||
return [i.second_ci_id for i in CIRelation.get_by(first_ci_id=ids[0], to_dict=False)]
|
|
||||||
|
|
||||||
else:
|
|
||||||
seconds = {i.second_ci_id for i in CIRelation.get_by(first_ci_id=ids[0],
|
|
||||||
ancestor_ids=self.ancestor_ids,
|
|
||||||
to_dict=False)}
|
|
||||||
|
|
||||||
return list(seconds)
|
|
||||||
|
|
||||||
merge_ids = []
|
merge_ids = []
|
||||||
key = []
|
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
||||||
_tmp = []
|
|
||||||
for level in range(1, sorted(self.level)[-1] + 1):
|
for level in range(1, sorted(self.level)[-1] + 1):
|
||||||
if not self.has_m2m:
|
_tmp = list(map(lambda x: list(json.loads(x).keys()),
|
||||||
_tmp = map(lambda x: json.loads(x).keys(),
|
filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION) or [])))
|
||||||
filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION) or []))
|
|
||||||
ids = [j for i in _tmp for j in i]
|
|
||||||
key, prefix = ids, REDIS_PREFIX_CI_RELATION
|
|
||||||
|
|
||||||
else:
|
|
||||||
if not self.ancestor_ids:
|
|
||||||
if level == 1:
|
|
||||||
key, prefix = list(map(str, ids)), REDIS_PREFIX_CI_RELATION
|
|
||||||
else:
|
|
||||||
key = list(set(["{},{}".format(i, j) for idx, i in enumerate(key) for j in _tmp[idx]]))
|
|
||||||
prefix = REDIS_PREFIX_CI_RELATION2
|
|
||||||
else:
|
|
||||||
if level == 1:
|
|
||||||
key, prefix = ["{},{}".format(self.ancestor_ids, i) for i in ids], REDIS_PREFIX_CI_RELATION2
|
|
||||||
else:
|
|
||||||
key = list(set(["{},{}".format(i, j) for idx, i in enumerate(key) for j in _tmp[idx]]))
|
|
||||||
prefix = REDIS_PREFIX_CI_RELATION2
|
|
||||||
|
|
||||||
if not key:
|
|
||||||
return []
|
|
||||||
|
|
||||||
_tmp = list(map(lambda x: json.loads(x).keys() if x else [], rd.get(key, prefix) or []))
|
|
||||||
ids = [j for i in _tmp for j in i]
|
ids = [j for i in _tmp for j in i]
|
||||||
|
|
||||||
if level in self.level:
|
if level in self.level:
|
||||||
merge_ids.extend(ids)
|
merge_ids.extend(ids)
|
||||||
|
|
||||||
return merge_ids
|
return merge_ids
|
||||||
|
|
||||||
def _get_reverse_ids(self, ids):
|
def _get_reverse_ids(self):
|
||||||
merge_ids = []
|
merge_ids = []
|
||||||
level2ids = {}
|
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
||||||
for level in range(1, sorted(self.level)[-1] + 1):
|
for level in range(1, sorted(self.level)[-1] + 1):
|
||||||
ids, _level2ids = CIRelationManager.get_ancestor_ids(ids, 1)
|
ids = CIRelationManager.get_ancestor_ids(ids, 1)
|
||||||
|
|
||||||
if _level2ids.get(2):
|
|
||||||
level2ids[level + 1] = _level2ids[2]
|
|
||||||
|
|
||||||
if level in self.level:
|
if level in self.level:
|
||||||
if level in level2ids and level2ids[level]:
|
merge_ids.extend(ids)
|
||||||
merge_ids.extend(set(ids) & set(level2ids[level]))
|
|
||||||
else:
|
|
||||||
merge_ids.extend(ids)
|
|
||||||
|
|
||||||
return merge_ids
|
return merge_ids
|
||||||
|
|
||||||
@@ -122,7 +64,7 @@ class Search(object):
|
|||||||
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
||||||
cis = [CI.get_by_id(_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(_id))) for _id in ids]
|
cis = [CI.get_by_id(_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(_id))) for _id in ids]
|
||||||
|
|
||||||
merge_ids = self._get_ids(ids) if not self.reverse else self._get_reverse_ids(ids)
|
merge_ids = self._get_ids() if not self.reverse else self._get_reverse_ids()
|
||||||
|
|
||||||
if not self.orig_query or ("_type:" not in self.orig_query
|
if not self.orig_query or ("_type:" not in self.orig_query
|
||||||
and "type_id:" not in self.orig_query
|
and "type_id:" not in self.orig_query
|
||||||
@@ -134,11 +76,11 @@ class Search(object):
|
|||||||
type_ids.extend(CITypeRelationManager.get_child_type_ids(ci.type_id, level))
|
type_ids.extend(CITypeRelationManager.get_child_type_ids(ci.type_id, level))
|
||||||
else:
|
else:
|
||||||
type_ids.extend(CITypeRelationManager.get_parent_type_ids(ci.type_id, level))
|
type_ids.extend(CITypeRelationManager.get_parent_type_ids(ci.type_id, level))
|
||||||
type_ids = set(type_ids)
|
type_ids = list(set(type_ids))
|
||||||
if self.orig_query:
|
if self.orig_query:
|
||||||
self.orig_query = "_type:({0}),{1}".format(";".join(map(str, type_ids)), self.orig_query)
|
self.orig_query = "_type:({0}),{1}".format(";".join(list(map(str, type_ids))), self.orig_query)
|
||||||
else:
|
else:
|
||||||
self.orig_query = "_type:({0})".format(";".join(map(str, type_ids)))
|
self.orig_query = "_type:({0})".format(";".join(list(map(str, type_ids))))
|
||||||
|
|
||||||
if not merge_ids:
|
if not merge_ids:
|
||||||
# cis, counter, total, self.page, numfound, facet_
|
# cis, counter, total, self.page, numfound, facet_
|
||||||
@@ -163,65 +105,35 @@ class Search(object):
|
|||||||
|
|
||||||
def statistics(self, type_ids):
|
def statistics(self, type_ids):
|
||||||
self.level = int(self.level)
|
self.level = int(self.level)
|
||||||
|
|
||||||
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
|
||||||
_tmp = []
|
_tmp = []
|
||||||
level2ids = {}
|
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
||||||
for lv in range(1, self.level + 1):
|
for lv in range(0, self.level):
|
||||||
level2ids[lv] = []
|
if not lv:
|
||||||
|
if type_ids and lv == self.level - 1:
|
||||||
if lv == 1:
|
|
||||||
if not self.has_m2m:
|
|
||||||
key, prefix = ids, REDIS_PREFIX_CI_RELATION
|
|
||||||
else:
|
|
||||||
if not self.ancestor_ids:
|
|
||||||
key, prefix = ids, REDIS_PREFIX_CI_RELATION
|
|
||||||
else:
|
|
||||||
key = ["{},{}".format(self.ancestor_ids, _id) for _id in ids]
|
|
||||||
prefix = REDIS_PREFIX_CI_RELATION2
|
|
||||||
|
|
||||||
level2ids[lv] = [[i] for i in key]
|
|
||||||
|
|
||||||
if not key:
|
|
||||||
_tmp = []
|
|
||||||
continue
|
|
||||||
|
|
||||||
if type_ids and lv == self.level:
|
|
||||||
_tmp = list(map(lambda x: [i for i in x if i[1] in type_ids],
|
_tmp = list(map(lambda x: [i for i in x if i[1] in type_ids],
|
||||||
(map(lambda x: list(json.loads(x).items()),
|
(map(lambda x: list(json.loads(x).items()),
|
||||||
[i or '{}' for i in rd.get(key, prefix) or []]))))
|
[i or '{}' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []]))))
|
||||||
else:
|
else:
|
||||||
_tmp = list(map(lambda x: list(json.loads(x).items()),
|
_tmp = list(map(lambda x: list(json.loads(x).items()),
|
||||||
[i or '{}' for i in rd.get(key, prefix) or []]))
|
[i or '{}' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []]))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for idx, item in enumerate(_tmp):
|
for idx, item in enumerate(_tmp):
|
||||||
if item:
|
if item:
|
||||||
if not self.has_m2m:
|
if type_ids and lv == self.level - 1:
|
||||||
key, prefix = [i[0] for i in item], REDIS_PREFIX_CI_RELATION
|
__tmp = list(
|
||||||
|
map(lambda x: [(_id, type_id) for _id, type_id in json.loads(x).items()
|
||||||
|
if type_id in type_ids],
|
||||||
|
filter(lambda x: x is not None,
|
||||||
|
rd.get([i[0] for i in item], REDIS_PREFIX_CI_RELATION) or [])))
|
||||||
else:
|
else:
|
||||||
key = list(set(['{},{}'.format(j, i[0]) for i in item for j in level2ids[lv - 1][idx]]))
|
|
||||||
prefix = REDIS_PREFIX_CI_RELATION2
|
|
||||||
|
|
||||||
level2ids[lv].append(key)
|
__tmp = list(map(lambda x: list(json.loads(x).items()),
|
||||||
|
filter(lambda x: x is not None,
|
||||||
if key:
|
rd.get([i[0] for i in item], REDIS_PREFIX_CI_RELATION) or [])))
|
||||||
if type_ids and lv == self.level:
|
|
||||||
__tmp = map(lambda x: [(_id, type_id) for _id, type_id in json.loads(x).items()
|
|
||||||
if type_id in type_ids],
|
|
||||||
filter(lambda x: x is not None,
|
|
||||||
rd.get(key, prefix) or []))
|
|
||||||
else:
|
|
||||||
__tmp = map(lambda x: list(json.loads(x).items()),
|
|
||||||
filter(lambda x: x is not None,
|
|
||||||
rd.get(key, prefix) or []))
|
|
||||||
else:
|
|
||||||
__tmp = []
|
|
||||||
|
|
||||||
_tmp[idx] = [j for i in __tmp for j in i]
|
_tmp[idx] = [j for i in __tmp for j in i]
|
||||||
else:
|
else:
|
||||||
_tmp[idx] = []
|
_tmp[idx] = []
|
||||||
level2ids[lv].append([])
|
|
||||||
|
|
||||||
result = {str(_id): len(_tmp[idx]) for idx, _id in enumerate(ids)}
|
result = {str(_id): len(_tmp[idx]) for idx, _id in enumerate(ids)}
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ import api.models.cmdb as model
|
|||||||
from api.lib.cmdb.cache import AttributeCache
|
from api.lib.cmdb.cache import AttributeCache
|
||||||
from api.lib.cmdb.const import ValueTypeEnum
|
from api.lib.cmdb.const import ValueTypeEnum
|
||||||
|
|
||||||
TIME_RE = re.compile(r'(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d')
|
TIME_RE = re.compile(r"^20|21|22|23|[0-1]\d:[0-5]\d:[0-5]\d$")
|
||||||
|
|
||||||
|
|
||||||
def string2int(x):
|
def string2int(x):
|
||||||
|
@@ -80,22 +80,20 @@ class ACLManager(object):
|
|||||||
return role.to_dict()
|
return role.to_dict()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_role(_id):
|
def delete_role(_id, payload):
|
||||||
RoleCRUD.delete_role(_id)
|
RoleCRUD.delete_role(_id)
|
||||||
return dict(rid=_id)
|
return dict(rid=_id)
|
||||||
|
|
||||||
def get_user_info(self, username):
|
def get_user_info(self, username):
|
||||||
from api.lib.perm.acl.acl import ACLManager as ACL
|
from api.lib.perm.acl.acl import ACLManager as ACL
|
||||||
user_info = ACL().get_user_info(username, self.app_name)
|
user_info = ACL().get_user_info(username, self.app_name)
|
||||||
result = dict(
|
result = dict(name=user_info.get('nickname') or username,
|
||||||
name=user_info.get('nickname') or username,
|
username=user_info.get('username') or username,
|
||||||
username=user_info.get('username') or username,
|
email=user_info.get('email'),
|
||||||
email=user_info.get('email'),
|
uid=user_info.get('uid'),
|
||||||
uid=user_info.get('uid'),
|
rid=user_info.get('rid'),
|
||||||
rid=user_info.get('rid'),
|
role=dict(permissions=user_info.get('parents')),
|
||||||
role=dict(permissions=user_info.get('parents')),
|
avatar=user_info.get('avatar'))
|
||||||
avatar=user_info.get('avatar')
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@@ -1,24 +1,14 @@
|
|||||||
import copy
|
from flask import abort
|
||||||
import json
|
|
||||||
|
|
||||||
from flask import abort, current_app
|
|
||||||
from ldap3 import Connection
|
|
||||||
from ldap3 import Server
|
|
||||||
from ldap3.core.exceptions import LDAPBindError, LDAPSocketOpenError
|
|
||||||
from ldap3 import AUTO_BIND_NO_TLS
|
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
from api.models.common_setting import CommonData
|
from api.models.common_setting import CommonData
|
||||||
from api.lib.utils import AESCrypto
|
|
||||||
from api.lib.common_setting.const import AuthCommonConfig, AuthenticateType, AuthCommonConfigAutoRedirect, TestType
|
|
||||||
|
|
||||||
|
|
||||||
class CommonDataCRUD(object):
|
class CommonDataCRUD(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_data_by_type(data_type):
|
def get_data_by_type(data_type):
|
||||||
CommonDataCRUD.check_auth_type(data_type)
|
|
||||||
return CommonData.get_by(data_type=data_type)
|
return CommonData.get_by(data_type=data_type)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -28,8 +18,6 @@ class CommonDataCRUD(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def create_new_data(data_type, **kwargs):
|
def create_new_data(data_type, **kwargs):
|
||||||
try:
|
try:
|
||||||
CommonDataCRUD.check_auth_type(data_type)
|
|
||||||
|
|
||||||
return CommonData.create(data_type=data_type, **kwargs)
|
return CommonData.create(data_type=data_type, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
@@ -41,7 +29,6 @@ class CommonDataCRUD(object):
|
|||||||
if not existed:
|
if not existed:
|
||||||
abort(404, ErrFormat.common_data_not_found.format(_id))
|
abort(404, ErrFormat.common_data_not_found.format(_id))
|
||||||
try:
|
try:
|
||||||
CommonDataCRUD.check_auth_type(existed.data_type)
|
|
||||||
return existed.update(**kwargs)
|
return existed.update(**kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
@@ -53,230 +40,7 @@ class CommonDataCRUD(object):
|
|||||||
if not existed:
|
if not existed:
|
||||||
abort(404, ErrFormat.common_data_not_found.format(_id))
|
abort(404, ErrFormat.common_data_not_found.format(_id))
|
||||||
try:
|
try:
|
||||||
CommonDataCRUD.check_auth_type(existed.data_type)
|
|
||||||
existed.soft_delete()
|
existed.soft_delete()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
abort(400, str(e))
|
abort(400, str(e))
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_auth_type(data_type):
|
|
||||||
if data_type in list(AuthenticateType.all()) + [AuthCommonConfig]:
|
|
||||||
abort(400, ErrFormat.common_data_not_support_auth_type.format(data_type))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_auth_auto_redirect_enable(_value: int):
|
|
||||||
existed = CommonData.get_by(first=True, data_type=AuthCommonConfig, to_dict=False)
|
|
||||||
if not existed:
|
|
||||||
CommonDataCRUD.create_new_data(AuthCommonConfig, data={AuthCommonConfigAutoRedirect: _value})
|
|
||||||
else:
|
|
||||||
data = existed.data
|
|
||||||
data = copy.deepcopy(existed.data) if data else {}
|
|
||||||
data[AuthCommonConfigAutoRedirect] = _value
|
|
||||||
CommonDataCRUD.update_data(existed.id, data=data)
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_auth_auto_redirect_enable():
|
|
||||||
existed = CommonData.get_by(first=True, data_type=AuthCommonConfig)
|
|
||||||
if not existed:
|
|
||||||
return 0
|
|
||||||
data = existed.get('data', {})
|
|
||||||
if not data:
|
|
||||||
return 0
|
|
||||||
return data.get(AuthCommonConfigAutoRedirect, 0)
|
|
||||||
|
|
||||||
|
|
||||||
class AuthenticateDataCRUD(object):
|
|
||||||
common_type_list = [AuthCommonConfig]
|
|
||||||
|
|
||||||
def __init__(self, _type):
|
|
||||||
self._type = _type
|
|
||||||
self.record = None
|
|
||||||
self.decrypt_data = {}
|
|
||||||
|
|
||||||
def get_support_type_list(self):
|
|
||||||
return list(AuthenticateType.all()) + self.common_type_list
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
if not self.decrypt_data:
|
|
||||||
self.decrypt_data = self.get_decrypt_data()
|
|
||||||
|
|
||||||
return self.decrypt_data
|
|
||||||
|
|
||||||
def get_by_key(self, _key):
|
|
||||||
if not self.decrypt_data:
|
|
||||||
self.decrypt_data = self.get_decrypt_data()
|
|
||||||
|
|
||||||
return self.decrypt_data.get(_key, None)
|
|
||||||
|
|
||||||
def get_record(self, to_dict=False) -> CommonData:
|
|
||||||
return CommonData.get_by(first=True, data_type=self._type, to_dict=to_dict)
|
|
||||||
|
|
||||||
def get_record_with_decrypt(self) -> dict:
|
|
||||||
record = CommonData.get_by(first=True, data_type=self._type, to_dict=True)
|
|
||||||
if not record:
|
|
||||||
return {}
|
|
||||||
data = self.get_decrypt_dict(record.get('data', ''))
|
|
||||||
record['data'] = data
|
|
||||||
return record
|
|
||||||
|
|
||||||
def get_decrypt_dict(self, data):
|
|
||||||
decrypt_str = self.decrypt(data)
|
|
||||||
try:
|
|
||||||
return json.loads(decrypt_str)
|
|
||||||
except Exception as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
def get_decrypt_data(self) -> dict:
|
|
||||||
self.record = self.get_record()
|
|
||||||
if not self.record:
|
|
||||||
return self.get_from_config()
|
|
||||||
return self.get_decrypt_dict(self.record.data)
|
|
||||||
|
|
||||||
def get_from_config(self):
|
|
||||||
return current_app.config.get(self._type, {})
|
|
||||||
|
|
||||||
def check_by_type(self) -> None:
|
|
||||||
existed = self.get_record()
|
|
||||||
if existed:
|
|
||||||
abort(400, ErrFormat.common_data_already_existed.format(self._type))
|
|
||||||
|
|
||||||
def create(self, data) -> CommonData:
|
|
||||||
self.check_by_type()
|
|
||||||
encrypt = data.pop('encrypt', None)
|
|
||||||
if encrypt is False:
|
|
||||||
return CommonData.create(data_type=self._type, data=data)
|
|
||||||
encrypted_data = self.encrypt(data)
|
|
||||||
try:
|
|
||||||
return CommonData.create(data_type=self._type, data=encrypted_data)
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
def update_by_record(self, record, data) -> CommonData:
|
|
||||||
encrypt = data.pop('encrypt', None)
|
|
||||||
if encrypt is False:
|
|
||||||
return record.update(data=data)
|
|
||||||
encrypted_data = self.encrypt(data)
|
|
||||||
try:
|
|
||||||
return record.update(data=encrypted_data)
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
def update(self, _id, data) -> CommonData:
|
|
||||||
existed = CommonData.get_by(first=True, to_dict=False, id=_id)
|
|
||||||
if not existed:
|
|
||||||
abort(404, ErrFormat.common_data_not_found.format(_id))
|
|
||||||
|
|
||||||
return self.update_by_record(existed, data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete(_id) -> None:
|
|
||||||
existed = CommonData.get_by(first=True, to_dict=False, id=_id)
|
|
||||||
if not existed:
|
|
||||||
abort(404, ErrFormat.common_data_not_found.format(_id))
|
|
||||||
try:
|
|
||||||
existed.soft_delete()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def encrypt(data) -> str:
|
|
||||||
if type(data) is dict:
|
|
||||||
try:
|
|
||||||
data = json.dumps(data)
|
|
||||||
except Exception as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
return AESCrypto().encrypt(data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def decrypt(data) -> str:
|
|
||||||
return AESCrypto().decrypt(data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_enable_list():
|
|
||||||
all_records = CommonData.query.filter(
|
|
||||||
CommonData.data_type.in_(AuthenticateType.all()),
|
|
||||||
CommonData.deleted == 0
|
|
||||||
).all()
|
|
||||||
enable_list = []
|
|
||||||
for auth_type in AuthenticateType.all():
|
|
||||||
record = list(filter(lambda x: x.data_type == auth_type, all_records))
|
|
||||||
if not record:
|
|
||||||
config = current_app.config.get(auth_type, None)
|
|
||||||
if not config:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if config.get('enable', False):
|
|
||||||
enable_list.append(dict(
|
|
||||||
auth_type=auth_type,
|
|
||||||
))
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
decrypt_data = json.loads(AuthenticateDataCRUD.decrypt(record[0].data))
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(e)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if decrypt_data.get('enable', 0) == 1:
|
|
||||||
enable_list.append(dict(
|
|
||||||
auth_type=auth_type,
|
|
||||||
))
|
|
||||||
|
|
||||||
auth_auto_redirect = CommonDataCRUD.get_auth_auto_redirect_enable()
|
|
||||||
|
|
||||||
return dict(
|
|
||||||
enable_list=enable_list,
|
|
||||||
auth_auto_redirect=auth_auto_redirect,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test(self, test_type, data):
|
|
||||||
type_lower = self._type.lower()
|
|
||||||
func_name = f'test_{type_lower}'
|
|
||||||
if hasattr(self, func_name):
|
|
||||||
try:
|
|
||||||
return getattr(self, f'test_{type_lower}')(test_type, data)
|
|
||||||
except Exception as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
abort(400, ErrFormat.not_support_test.format(self._type))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def test_ldap(test_type, data):
|
|
||||||
ldap_server = data.get('ldap_server')
|
|
||||||
ldap_user_dn = data.get('ldap_user_dn', '{}')
|
|
||||||
|
|
||||||
server = Server(ldap_server, connect_timeout=2)
|
|
||||||
if not server.check_availability():
|
|
||||||
raise Exception(ErrFormat.ldap_server_connect_not_available)
|
|
||||||
else:
|
|
||||||
if test_type == TestType.Connect:
|
|
||||||
return True
|
|
||||||
|
|
||||||
username = data.get('username', None)
|
|
||||||
if not username:
|
|
||||||
raise Exception(ErrFormat.ldap_test_username_required)
|
|
||||||
user = ldap_user_dn.format(username)
|
|
||||||
password = data.get('password', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
Connection(server, user=user, password=password, auto_bind=AUTO_BIND_NO_TLS)
|
|
||||||
except LDAPBindError:
|
|
||||||
ldap_domain = data.get('ldap_domain')
|
|
||||||
user_with_domain = f"{username}@{ldap_domain}"
|
|
||||||
try:
|
|
||||||
Connection(server, user=user_with_domain, password=password, auto_bind=AUTO_BIND_NO_TLS)
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(ErrFormat.ldap_test_unknown_error.format(str(e)))
|
|
||||||
|
|
||||||
except LDAPSocketOpenError:
|
|
||||||
raise Exception(ErrFormat.ldap_server_connect_timeout)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(ErrFormat.ldap_test_unknown_error.format(str(e)))
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
from api.extensions import cache
|
from api.extensions import cache
|
||||||
from api.models.common_setting import CompanyInfo
|
from api.models.common_setting import CompanyInfo
|
||||||
|
|
||||||
@@ -13,7 +11,6 @@ class CompanyInfoCRUD(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(**kwargs):
|
def create(**kwargs):
|
||||||
CompanyInfoCRUD.check_data(**kwargs)
|
|
||||||
res = CompanyInfo.create(**kwargs)
|
res = CompanyInfo.create(**kwargs)
|
||||||
CompanyInfoCache.refresh(res.info)
|
CompanyInfoCache.refresh(res.info)
|
||||||
return res
|
return res
|
||||||
@@ -25,26 +22,10 @@ class CompanyInfoCRUD(object):
|
|||||||
if not existed:
|
if not existed:
|
||||||
existed = CompanyInfoCRUD.create(**kwargs)
|
existed = CompanyInfoCRUD.create(**kwargs)
|
||||||
else:
|
else:
|
||||||
CompanyInfoCRUD.check_data(**kwargs)
|
|
||||||
existed = existed.update(**kwargs)
|
existed = existed.update(**kwargs)
|
||||||
CompanyInfoCache.refresh(existed.info)
|
CompanyInfoCache.refresh(existed.info)
|
||||||
return existed
|
return existed
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_data(**kwargs):
|
|
||||||
info = kwargs.get('info', {})
|
|
||||||
info['messenger'] = CompanyInfoCRUD.check_messenger(info.get('messenger', None))
|
|
||||||
|
|
||||||
kwargs['info'] = info
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_messenger(messenger):
|
|
||||||
if not messenger:
|
|
||||||
return messenger
|
|
||||||
|
|
||||||
parsed_url = urlparse(messenger)
|
|
||||||
return f"{parsed_url.scheme}://{parsed_url.netloc}"
|
|
||||||
|
|
||||||
|
|
||||||
class CompanyInfoCache(object):
|
class CompanyInfoCache(object):
|
||||||
key = 'CompanyInfoCache::'
|
key = 'CompanyInfoCache::'
|
||||||
@@ -60,4 +41,4 @@ class CompanyInfoCache(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def refresh(cls, info):
|
def refresh(cls, info):
|
||||||
cache.set(cls.key, info)
|
cache.set(cls.key, info)
|
@@ -19,19 +19,3 @@ BotNameMap = {
|
|||||||
'feishuApp': 'feishuBot',
|
'feishuApp': 'feishuBot',
|
||||||
'dingdingApp': 'dingdingBot',
|
'dingdingApp': 'dingdingBot',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AuthenticateType(BaseEnum):
|
|
||||||
CAS = 'CAS'
|
|
||||||
OAUTH2 = 'OAUTH2'
|
|
||||||
OIDC = 'OIDC'
|
|
||||||
LDAP = 'LDAP'
|
|
||||||
|
|
||||||
|
|
||||||
AuthCommonConfig = 'AuthCommonConfig'
|
|
||||||
AuthCommonConfigAutoRedirect = 'auto_redirect'
|
|
||||||
|
|
||||||
|
|
||||||
class TestType(BaseEnum):
|
|
||||||
Connect = 'connect'
|
|
||||||
Login = 'login'
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
from flask import abort, current_app
|
from flask import abort
|
||||||
from treelib import Tree
|
from treelib import Tree
|
||||||
from wtforms import Form
|
from wtforms import Form
|
||||||
from wtforms import IntegerField
|
from wtforms import IntegerField
|
||||||
@@ -9,7 +9,6 @@ from wtforms import validators
|
|||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
from api.lib.common_setting.acl import ACLManager
|
|
||||||
from api.lib.perm.acl.role import RoleCRUD
|
from api.lib.perm.acl.role import RoleCRUD
|
||||||
from api.models.common_setting import Department, Employee
|
from api.models.common_setting import Department, Employee
|
||||||
|
|
||||||
@@ -153,10 +152,6 @@ class DepartmentForm(Form):
|
|||||||
|
|
||||||
class DepartmentCRUD(object):
|
class DepartmentCRUD(object):
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_department_by_id(d_id, to_dict=True):
|
|
||||||
return Department.get_by(first=True, department_id=d_id, to_dict=to_dict)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add(**kwargs):
|
def add(**kwargs):
|
||||||
DepartmentCRUD.check_department_name_unique(kwargs['department_name'])
|
DepartmentCRUD.check_department_name_unique(kwargs['department_name'])
|
||||||
@@ -191,11 +186,10 @@ class DepartmentCRUD(object):
|
|||||||
filter(lambda d: d['department_id'] == department_parent_id, allow_p_d_id_list))
|
filter(lambda d: d['department_id'] == department_parent_id, allow_p_d_id_list))
|
||||||
if len(target) == 0:
|
if len(target) == 0:
|
||||||
try:
|
try:
|
||||||
dep = Department.get_by(
|
d = Department.get_by(
|
||||||
first=True, to_dict=False, department_id=department_parent_id)
|
first=True, to_dict=False, department_id=department_parent_id)
|
||||||
name = dep.department_name if dep else ErrFormat.department_id_not_found.format(department_parent_id)
|
name = d.department_name if d else ErrFormat.department_id_not_found.format(department_parent_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(str(e))
|
|
||||||
name = ErrFormat.department_id_not_found.format(department_parent_id)
|
name = ErrFormat.department_id_not_found.format(department_parent_id)
|
||||||
abort(400, ErrFormat.cannot_to_be_parent_department.format(name))
|
abort(400, ErrFormat.cannot_to_be_parent_department.format(name))
|
||||||
|
|
||||||
@@ -259,7 +253,7 @@ class DepartmentCRUD(object):
|
|||||||
try:
|
try:
|
||||||
RoleCRUD.delete_role(existed.acl_rid)
|
RoleCRUD.delete_role(existed.acl_rid)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(str(e))
|
pass
|
||||||
|
|
||||||
return existed.soft_delete()
|
return existed.soft_delete()
|
||||||
|
|
||||||
@@ -274,7 +268,7 @@ class DepartmentCRUD(object):
|
|||||||
try:
|
try:
|
||||||
tree.remove_subtree(department_id)
|
tree.remove_subtree(department_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(str(e))
|
pass
|
||||||
|
|
||||||
[allow_d_id_list.append({'department_id': int(n.identifier), 'department_name': n.tag}) for n in
|
[allow_d_id_list.append({'department_id': int(n.identifier), 'department_name': n.tag}) for n in
|
||||||
tree.all_nodes()]
|
tree.all_nodes()]
|
||||||
@@ -396,125 +390,6 @@ class DepartmentCRUD(object):
|
|||||||
[id_list.append(int(n.identifier))
|
[id_list.append(int(n.identifier))
|
||||||
for n in tmp_tree.all_nodes()]
|
for n in tmp_tree.all_nodes()]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(str(e))
|
pass
|
||||||
|
|
||||||
return id_list
|
return id_list
|
||||||
|
|
||||||
|
|
||||||
class EditDepartmentInACL(object):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_department_to_acl(department_id, op_uid):
|
|
||||||
db_department = DepartmentCRUD.get_department_by_id(department_id, to_dict=False)
|
|
||||||
if not db_department:
|
|
||||||
return
|
|
||||||
|
|
||||||
from api.models.acl import Role
|
|
||||||
role = Role.get_by(first=True, name=db_department.department_name, app_id=None)
|
|
||||||
|
|
||||||
acl = ACLManager('acl', str(op_uid))
|
|
||||||
if role is None:
|
|
||||||
payload = {
|
|
||||||
'app_id': 'acl',
|
|
||||||
'name': db_department.department_name,
|
|
||||||
}
|
|
||||||
role = acl.create_role(payload)
|
|
||||||
|
|
||||||
acl_rid = role.get('id') if role else 0
|
|
||||||
|
|
||||||
db_department.update(
|
|
||||||
acl_rid=acl_rid
|
|
||||||
)
|
|
||||||
info = f"add_department_to_acl, acl_rid: {acl_rid}"
|
|
||||||
current_app.logger.info(info)
|
|
||||||
return info
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete_department_from_acl(department_rids, op_uid):
|
|
||||||
acl = ACLManager('acl', str(op_uid))
|
|
||||||
|
|
||||||
result = []
|
|
||||||
|
|
||||||
for rid in department_rids:
|
|
||||||
try:
|
|
||||||
acl.delete_role(rid)
|
|
||||||
except Exception as e:
|
|
||||||
result.append(f"delete_department_in_acl, rid: {rid}, error: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def edit_department_name_in_acl(d_rid: int, d_name: str, op_uid: int):
|
|
||||||
acl = ACLManager('acl', str(op_uid))
|
|
||||||
payload = {
|
|
||||||
'name': d_name
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
acl.edit_role(d_rid, payload)
|
|
||||||
except Exception as e:
|
|
||||||
return f"edit_department_name_in_acl, rid: {d_rid}, error: {e}"
|
|
||||||
|
|
||||||
return f"edit_department_name_in_acl, rid: {d_rid}, success"
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def edit_employee_department_in_acl(e_list: list, new_d_id: int, op_uid: int):
|
|
||||||
result = []
|
|
||||||
new_department = DepartmentCRUD.get_department_by_id(new_d_id, False)
|
|
||||||
if not new_department:
|
|
||||||
result.append(f"{new_d_id} new_department is None")
|
|
||||||
return result
|
|
||||||
|
|
||||||
from api.models.acl import Role
|
|
||||||
new_role = Role.get_by(first=True, name=new_department.department_name, app_id=None)
|
|
||||||
new_d_rid_in_acl = new_role.get('id') if new_role else 0
|
|
||||||
if new_d_rid_in_acl == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
if new_d_rid_in_acl != new_department.acl_rid:
|
|
||||||
new_department.update(
|
|
||||||
acl_rid=new_d_rid_in_acl
|
|
||||||
)
|
|
||||||
new_department_acl_rid = new_department.acl_rid if new_d_rid_in_acl == new_department.acl_rid else \
|
|
||||||
new_d_rid_in_acl
|
|
||||||
|
|
||||||
acl = ACLManager('acl', str(op_uid))
|
|
||||||
for employee in e_list:
|
|
||||||
old_department = DepartmentCRUD.get_department_by_id(employee.get('department_id'), False)
|
|
||||||
if not old_department:
|
|
||||||
continue
|
|
||||||
employee_acl_rid = employee.get('e_acl_rid')
|
|
||||||
if employee_acl_rid == 0:
|
|
||||||
result.append(f"employee_acl_rid == 0")
|
|
||||||
continue
|
|
||||||
|
|
||||||
old_role = Role.get_by(first=True, name=old_department.department_name, app_id=None)
|
|
||||||
old_d_rid_in_acl = old_role.get('id') if old_role else 0
|
|
||||||
if old_d_rid_in_acl == 0:
|
|
||||||
return
|
|
||||||
if old_d_rid_in_acl != old_department.acl_rid:
|
|
||||||
old_department.update(
|
|
||||||
acl_rid=old_d_rid_in_acl
|
|
||||||
)
|
|
||||||
d_acl_rid = old_department.acl_rid if old_d_rid_in_acl == old_department.acl_rid else old_d_rid_in_acl
|
|
||||||
payload = {
|
|
||||||
'app_id': 'acl',
|
|
||||||
'parent_id': d_acl_rid,
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
acl.remove_user_from_role(employee_acl_rid, payload)
|
|
||||||
except Exception as e:
|
|
||||||
result.append(
|
|
||||||
f"remove_user_from_role employee_acl_rid: {employee_acl_rid}, parent_id: {d_acl_rid}, err: {e}")
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
'app_id': 'acl',
|
|
||||||
'child_ids': [employee_acl_rid],
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
acl.add_user_to_role(new_department_acl_rid, payload)
|
|
||||||
except Exception as e:
|
|
||||||
result.append(
|
|
||||||
f"add_user_to_role employee_acl_rid: {employee_acl_rid}, parent_id: {d_acl_rid}, err: {e}")
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
@@ -178,7 +178,7 @@ class EmployeeCRUD(object):
|
|||||||
def edit_employee_by_uid(_uid, **kwargs):
|
def edit_employee_by_uid(_uid, **kwargs):
|
||||||
existed = EmployeeCRUD.get_employee_by_uid(_uid)
|
existed = EmployeeCRUD.get_employee_by_uid(_uid)
|
||||||
try:
|
try:
|
||||||
edit_acl_user(_uid, **kwargs)
|
user = edit_acl_user(_uid, **kwargs)
|
||||||
|
|
||||||
for column in employee_pop_columns:
|
for column in employee_pop_columns:
|
||||||
if kwargs.get(column):
|
if kwargs.get(column):
|
||||||
@@ -190,9 +190,9 @@ class EmployeeCRUD(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def change_password_by_uid(_uid, password):
|
def change_password_by_uid(_uid, password):
|
||||||
EmployeeCRUD.get_employee_by_uid(_uid)
|
existed = EmployeeCRUD.get_employee_by_uid(_uid)
|
||||||
try:
|
try:
|
||||||
edit_acl_user(_uid, password=password)
|
user = edit_acl_user(_uid, password=password)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return abort(400, str(e))
|
return abort(400, str(e))
|
||||||
|
|
||||||
@@ -359,11 +359,9 @@ class EmployeeCRUD(object):
|
|||||||
|
|
||||||
if value and column == "last_login":
|
if value and column == "last_login":
|
||||||
try:
|
try:
|
||||||
return datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
|
value = datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
err = f"{ErrFormat.datetime_format_error.format(column)}: {str(e)}"
|
abort(400, ErrFormat.datetime_format_error.format(column))
|
||||||
abort(400, err)
|
|
||||||
return value
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_attr_by_column(column):
|
def get_attr_by_column(column):
|
||||||
@@ -384,7 +382,7 @@ class EmployeeCRUD(object):
|
|||||||
relation = condition.get("relation", None)
|
relation = condition.get("relation", None)
|
||||||
value = condition.get("value", None)
|
value = condition.get("value", None)
|
||||||
|
|
||||||
value = EmployeeCRUD.check_condition(column, operator, value, relation)
|
EmployeeCRUD.check_condition(column, operator, value, relation)
|
||||||
a, o = EmployeeCRUD.get_expr_by_condition(
|
a, o = EmployeeCRUD.get_expr_by_condition(
|
||||||
column, operator, value, relation)
|
column, operator, value, relation)
|
||||||
and_list += a
|
and_list += a
|
||||||
@@ -563,130 +561,10 @@ class EmployeeCRUD(object):
|
|||||||
for column in direct_columns:
|
for column in direct_columns:
|
||||||
tmp[column] = d.get(column, '')
|
tmp[column] = d.get(column, '')
|
||||||
notice_info = d.get('notice_info', {})
|
notice_info = d.get('notice_info', {})
|
||||||
notice_info = copy.deepcopy(notice_info) if notice_info else {}
|
|
||||||
tmp.update(**notice_info)
|
tmp.update(**notice_info)
|
||||||
results.append(tmp)
|
results.append(tmp)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def import_employee(employee_list):
|
|
||||||
res = CreateEmployee().batch_create(employee_list)
|
|
||||||
return res
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def batch_edit_employee_department(employee_id_list, column_value):
|
|
||||||
err_list = []
|
|
||||||
employee_list = []
|
|
||||||
for _id in employee_id_list:
|
|
||||||
try:
|
|
||||||
existed = EmployeeCRUD.get_employee_by_id(_id)
|
|
||||||
employee = dict(
|
|
||||||
e_acl_rid=existed.acl_rid,
|
|
||||||
department_id=existed.department_id
|
|
||||||
)
|
|
||||||
employee_list.append(employee)
|
|
||||||
existed.update(department_id=column_value)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
err_list.append({
|
|
||||||
'employee_id': _id,
|
|
||||||
'err': str(e),
|
|
||||||
})
|
|
||||||
from api.lib.common_setting.department import EditDepartmentInACL
|
|
||||||
EditDepartmentInACL.edit_employee_department_in_acl(
|
|
||||||
employee_list, column_value, current_user.uid
|
|
||||||
)
|
|
||||||
return err_list
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def batch_edit_password_or_block_column(column_name, employee_id_list, column_value, is_acl=False):
|
|
||||||
if column_name == 'block':
|
|
||||||
err_list = []
|
|
||||||
success_list = []
|
|
||||||
for _id in employee_id_list:
|
|
||||||
try:
|
|
||||||
employee = EmployeeCRUD.edit_employee_block_column(
|
|
||||||
_id, is_acl, **{column_name: column_value})
|
|
||||||
success_list.append(employee)
|
|
||||||
except Exception as e:
|
|
||||||
err_list.append({
|
|
||||||
'employee_id': _id,
|
|
||||||
'err': str(e),
|
|
||||||
})
|
|
||||||
return err_list
|
|
||||||
else:
|
|
||||||
return EmployeeCRUD.batch_edit_column(column_name, employee_id_list, column_value, is_acl)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def batch_edit_column(column_name, employee_id_list, column_value, is_acl=False):
|
|
||||||
err_list = []
|
|
||||||
for _id in employee_id_list:
|
|
||||||
try:
|
|
||||||
EmployeeCRUD.edit_employee_single_column(
|
|
||||||
_id, is_acl, **{column_name: column_value})
|
|
||||||
except Exception as e:
|
|
||||||
err_list.append({
|
|
||||||
'employee_id': _id,
|
|
||||||
'err': str(e),
|
|
||||||
})
|
|
||||||
|
|
||||||
return err_list
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def edit_employee_single_column(_id, is_acl=False, **kwargs):
|
|
||||||
existed = EmployeeCRUD.get_employee_by_id(_id)
|
|
||||||
if 'direct_supervisor_id' in kwargs.keys():
|
|
||||||
if kwargs['direct_supervisor_id'] == existed.direct_supervisor_id:
|
|
||||||
raise Exception(ErrFormat.direct_supervisor_is_not_self)
|
|
||||||
|
|
||||||
if is_acl:
|
|
||||||
return edit_acl_user(existed.acl_uid, **kwargs)
|
|
||||||
|
|
||||||
try:
|
|
||||||
for column in employee_pop_columns:
|
|
||||||
if kwargs.get(column):
|
|
||||||
kwargs.pop(column)
|
|
||||||
|
|
||||||
return existed.update(**kwargs)
|
|
||||||
except Exception as e:
|
|
||||||
return abort(400, str(e))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def edit_employee_block_column(_id, is_acl=False, **kwargs):
|
|
||||||
existed = EmployeeCRUD.get_employee_by_id(_id)
|
|
||||||
value = get_block_value(kwargs.get('block'))
|
|
||||||
if value is True:
|
|
||||||
check_department_director_id_or_direct_supervisor_id(_id)
|
|
||||||
value = 1
|
|
||||||
else:
|
|
||||||
value = 0
|
|
||||||
|
|
||||||
if is_acl:
|
|
||||||
kwargs['block'] = value
|
|
||||||
edit_acl_user(existed.acl_uid, **kwargs)
|
|
||||||
|
|
||||||
existed.update(block=value)
|
|
||||||
data = existed.to_dict()
|
|
||||||
return data
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def batch_employee(column_name, column_value, employee_id_list):
|
|
||||||
if column_value is None:
|
|
||||||
abort(400, ErrFormat.value_is_required)
|
|
||||||
if column_name in ['password', 'block']:
|
|
||||||
return EmployeeCRUD.batch_edit_password_or_block_column(column_name, employee_id_list, column_value, True)
|
|
||||||
|
|
||||||
elif column_name in ['department_id']:
|
|
||||||
return EmployeeCRUD.batch_edit_employee_department(employee_id_list, column_value)
|
|
||||||
|
|
||||||
elif column_name in [
|
|
||||||
'direct_supervisor_id', 'position_name'
|
|
||||||
]:
|
|
||||||
return EmployeeCRUD.batch_edit_column(column_name, employee_id_list, column_value, False)
|
|
||||||
|
|
||||||
else:
|
|
||||||
abort(400, ErrFormat.column_name_not_support)
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_map(key='uid', acl=None):
|
def get_user_map(key='uid', acl=None):
|
||||||
"""
|
"""
|
||||||
@@ -727,7 +605,6 @@ class CreateEmployee(object):
|
|||||||
try:
|
try:
|
||||||
existed = self.check_acl_user(user_data)
|
existed = self.check_acl_user(user_data)
|
||||||
if not existed:
|
if not existed:
|
||||||
user_data['add_from'] = 'common'
|
|
||||||
return self.acl.create_user(user_data)
|
return self.acl.create_user(user_data)
|
||||||
return existed
|
return existed
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -764,8 +641,7 @@ class CreateEmployee(object):
|
|||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
def get_department_by_name(self, d_name):
|
||||||
def get_department_by_name(d_name):
|
|
||||||
return Department.get_by(first=True, department_name=d_name)
|
return Department.get_by(first=True, department_name=d_name)
|
||||||
|
|
||||||
def get_end_department_id(self, department_name_list, department_name_map):
|
def get_end_department_id(self, department_name_list, department_name_map):
|
||||||
|
@@ -162,4 +162,4 @@ class NoticeConfigUpdateForm(Form):
|
|||||||
info = StringField(validators=[
|
info = StringField(validators=[
|
||||||
validators.DataRequired(message="信息 不能为空"),
|
validators.DataRequired(message="信息 不能为空"),
|
||||||
validators.Length(max=255),
|
validators.Length(max=255),
|
||||||
])
|
])
|
@@ -8,9 +8,6 @@ class ErrFormat(CommonErrFormat):
|
|||||||
|
|
||||||
no_file_part = "没有文件部分"
|
no_file_part = "没有文件部分"
|
||||||
file_is_required = "文件是必须的"
|
file_is_required = "文件是必须的"
|
||||||
file_not_found = "文件不存在"
|
|
||||||
file_type_not_allowed = "文件类型不允许"
|
|
||||||
upload_failed = "上传失败: {}"
|
|
||||||
|
|
||||||
direct_supervisor_is_not_self = "直属上级不能是自己"
|
direct_supervisor_is_not_self = "直属上级不能是自己"
|
||||||
parent_department_is_not_self = "上级部门不能是自己"
|
parent_department_is_not_self = "上级部门不能是自己"
|
||||||
@@ -59,7 +56,6 @@ class ErrFormat(CommonErrFormat):
|
|||||||
email_send_timeout = "邮件发送超时"
|
email_send_timeout = "邮件发送超时"
|
||||||
|
|
||||||
common_data_not_found = "ID {} 找不到记录"
|
common_data_not_found = "ID {} 找不到记录"
|
||||||
common_data_already_existed = "{} 已存在"
|
|
||||||
notice_platform_existed = "{} 已存在"
|
notice_platform_existed = "{} 已存在"
|
||||||
notice_not_existed = "{} 配置项不存在"
|
notice_not_existed = "{} 配置项不存在"
|
||||||
notice_please_config_messenger_first = "请先配置 messenger"
|
notice_please_config_messenger_first = "请先配置 messenger"
|
||||||
@@ -67,11 +63,3 @@ class ErrFormat(CommonErrFormat):
|
|||||||
notice_bind_failed = "绑定失败: {}"
|
notice_bind_failed = "绑定失败: {}"
|
||||||
notice_bind_success = "绑定成功"
|
notice_bind_success = "绑定成功"
|
||||||
notice_remove_bind_success = "解绑成功"
|
notice_remove_bind_success = "解绑成功"
|
||||||
|
|
||||||
not_support_test = "不支持的测试类型: {}"
|
|
||||||
not_support_auth_type = "不支持的认证类型: {}"
|
|
||||||
ldap_server_connect_timeout = "LDAP服务器连接超时"
|
|
||||||
ldap_server_connect_not_available = "LDAP服务器连接不可用"
|
|
||||||
ldap_test_unknown_error = "LDAP测试未知错误: {}"
|
|
||||||
common_data_not_support_auth_type = "通用数据不支持auth类型: {}"
|
|
||||||
ldap_test_username_required = "LDAP测试用户名必填"
|
|
||||||
|
@@ -1,13 +1,6 @@
|
|||||||
import uuid
|
import uuid
|
||||||
import os
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
from flask import abort, current_app
|
|
||||||
import lz4.frame
|
|
||||||
|
|
||||||
from api.lib.common_setting.utils import get_cur_time_str
|
from api.lib.common_setting.utils import get_cur_time_str
|
||||||
from api.models.common_setting import CommonFile
|
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
|
||||||
|
|
||||||
|
|
||||||
def allowed_file(filename, allowed_extensions):
|
def allowed_file(filename, allowed_extensions):
|
||||||
@@ -21,48 +14,3 @@ def generate_new_file_name(name):
|
|||||||
cur_str = get_cur_time_str('_')
|
cur_str = get_cur_time_str('_')
|
||||||
|
|
||||||
return f"{prev_name}_{cur_str}_{uid}.{ext}"
|
return f"{prev_name}_{cur_str}_{uid}.{ext}"
|
||||||
|
|
||||||
|
|
||||||
class CommonFileCRUD:
|
|
||||||
@staticmethod
|
|
||||||
def add_file(**kwargs):
|
|
||||||
return CommonFile.create(**kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_file(file_name):
|
|
||||||
existed = CommonFile.get_by(file_name=file_name, first=True, to_dict=False)
|
|
||||||
if not existed:
|
|
||||||
abort(400, ErrFormat.file_not_found)
|
|
||||||
|
|
||||||
uncompressed_data = lz4.frame.decompress(existed.binary)
|
|
||||||
|
|
||||||
return BytesIO(uncompressed_data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def sync_file_to_db():
|
|
||||||
for p in ['UPLOAD_DIRECTORY_FULL']:
|
|
||||||
upload_path = current_app.config.get(p, None)
|
|
||||||
if not upload_path:
|
|
||||||
continue
|
|
||||||
for root, dirs, files in os.walk(upload_path):
|
|
||||||
for file in files:
|
|
||||||
file_path = os.path.join(root, file)
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
continue
|
|
||||||
|
|
||||||
existed = CommonFile.get_by(file_name=file, first=True, to_dict=False)
|
|
||||||
if existed:
|
|
||||||
continue
|
|
||||||
with open(file_path, 'rb') as f:
|
|
||||||
data = f.read()
|
|
||||||
compressed_data = lz4.frame.compress(data)
|
|
||||||
try:
|
|
||||||
CommonFileCRUD.add_file(
|
|
||||||
origin_name=file,
|
|
||||||
file_name=file,
|
|
||||||
binary=compressed_data
|
|
||||||
)
|
|
||||||
|
|
||||||
current_app.logger.info(f'sync file {file} to db')
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(f'sync file {file} to db error: {e}')
|
|
||||||
|
@@ -10,18 +10,14 @@ from api.lib.exception import CommitException
|
|||||||
|
|
||||||
class FormatMixin(object):
|
class FormatMixin(object):
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
res = dict()
|
res = dict([(k, getattr(self, k) if not isinstance(
|
||||||
for k in getattr(self, "__mapper__").c.keys():
|
getattr(self, k), (datetime.datetime, datetime.date, datetime.time)) else str(
|
||||||
if k in {'password', '_password', 'secret', '_secret'}:
|
getattr(self, k))) for k in getattr(self, "__mapper__").c.keys()])
|
||||||
continue
|
# FIXME: getattr(cls, "__table__").columns k.name
|
||||||
|
|
||||||
if k.startswith('_'):
|
res.pop('password', None)
|
||||||
k = k[1:]
|
res.pop('_password', None)
|
||||||
|
res.pop('secret', None)
|
||||||
if not isinstance(getattr(self, k), (datetime.datetime, datetime.date, datetime.time)):
|
|
||||||
res[k] = getattr(self, k)
|
|
||||||
else:
|
|
||||||
res[k] = str(getattr(self, k))
|
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -94,7 +90,7 @@ class CRUDMixin(FormatMixin):
|
|||||||
if any((isinstance(_id, six.string_types) and _id.isdigit(),
|
if any((isinstance(_id, six.string_types) and _id.isdigit(),
|
||||||
isinstance(_id, (six.integer_types, float))), ):
|
isinstance(_id, (six.integer_types, float))), ):
|
||||||
obj = getattr(cls, "query").get(int(_id))
|
obj = getattr(cls, "query").get(int(_id))
|
||||||
if obj and not getattr(obj, 'deleted', False):
|
if obj and not obj.deleted:
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@@ -1,19 +1,14 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
import datetime
|
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from flask import has_request_context
|
from flask import has_request_context, request
|
||||||
from flask import request
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
|
|
||||||
from api.extensions import db
|
|
||||||
from api.lib.perm.acl import AppCache
|
from api.lib.perm.acl import AppCache
|
||||||
from api.models.acl import AuditLoginLog
|
|
||||||
from api.models.acl import AuditPermissionLog
|
from api.models.acl import AuditPermissionLog
|
||||||
from api.models.acl import AuditResourceLog
|
from api.models.acl import AuditResourceLog
|
||||||
from api.models.acl import AuditRoleLog
|
from api.models.acl import AuditRoleLog
|
||||||
@@ -288,27 +283,6 @@ class AuditCRUD(object):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def search_login(_, q=None, page=1, page_size=10, start=None, end=None):
|
|
||||||
query = db.session.query(AuditLoginLog)
|
|
||||||
|
|
||||||
if start:
|
|
||||||
query = query.filter(AuditLoginLog.login_at >= start)
|
|
||||||
if end:
|
|
||||||
query = query.filter(AuditLoginLog.login_at <= end)
|
|
||||||
|
|
||||||
if q:
|
|
||||||
query = query.filter(AuditLoginLog.username == q)
|
|
||||||
|
|
||||||
records = query.order_by(
|
|
||||||
AuditLoginLog.id.desc()).offset((page - 1) * page_size).limit(page_size).all()
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'data': [r.to_dict() for r in records],
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_role_log(cls, app_id, operate_type: AuditOperateType,
|
def add_role_log(cls, app_id, operate_type: AuditOperateType,
|
||||||
scope: AuditScope, link_id: int, origin: dict, current: dict, extra: dict,
|
scope: AuditScope, link_id: int, origin: dict, current: dict, extra: dict,
|
||||||
@@ -374,24 +348,3 @@ class AuditCRUD(object):
|
|||||||
AuditTriggerLog.create(app_id=app_id, trigger_id=trigger_id, operate_uid=user_id,
|
AuditTriggerLog.create(app_id=app_id, trigger_id=trigger_id, operate_uid=user_id,
|
||||||
operate_type=operate_type.value,
|
operate_type=operate_type.value,
|
||||||
origin=origin, current=current, extra=extra, source=source.value)
|
origin=origin, current=current, extra=extra, source=source.value)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def add_login_log(cls, username, is_ok, description, _id=None, logout_at=None):
|
|
||||||
if _id is not None:
|
|
||||||
existed = AuditLoginLog.get_by_id(_id)
|
|
||||||
if existed is not None:
|
|
||||||
existed.update(logout_at=logout_at)
|
|
||||||
return
|
|
||||||
|
|
||||||
payload = dict(username=username,
|
|
||||||
is_ok=is_ok,
|
|
||||||
description=description,
|
|
||||||
logout_at=logout_at,
|
|
||||||
ip=request.headers.get('X-Real-IP') or request.remote_addr,
|
|
||||||
browser=request.headers.get('User-Agent'),
|
|
||||||
)
|
|
||||||
|
|
||||||
if logout_at is None:
|
|
||||||
payload['login_at'] = datetime.datetime.now()
|
|
||||||
|
|
||||||
return AuditLoginLog.create(**payload).id
|
|
||||||
|
@@ -276,6 +276,7 @@ class ResourceCRUD(object):
|
|||||||
|
|
||||||
from api.tasks.acl import apply_trigger
|
from api.tasks.acl import apply_trigger
|
||||||
triggers = TriggerCRUD.match_triggers(app_id, r.name, r.resource_type_id, uid)
|
triggers = TriggerCRUD.match_triggers(app_id, r.name, r.resource_type_id, uid)
|
||||||
|
current_app.logger.info(triggers)
|
||||||
for trigger in triggers:
|
for trigger in triggers:
|
||||||
# auto trigger should be no uid
|
# auto trigger should be no uid
|
||||||
apply_trigger.apply_async(args=(trigger.id,),
|
apply_trigger.apply_async(args=(trigger.id,),
|
||||||
|
@@ -4,9 +4,6 @@ from api.lib.resp_format import CommonErrFormat
|
|||||||
|
|
||||||
|
|
||||||
class ErrFormat(CommonErrFormat):
|
class ErrFormat(CommonErrFormat):
|
||||||
login_succeed = "登录成功"
|
|
||||||
ldap_connection_failed = "连接LDAP服务失败"
|
|
||||||
invalid_password = "密码验证失败"
|
|
||||||
auth_only_with_app_token_failed = "应用 Token验证失败"
|
auth_only_with_app_token_failed = "应用 Token验证失败"
|
||||||
session_invalid = "您不是应用管理员 或者 session失效(尝试一下退出重新登录)"
|
session_invalid = "您不是应用管理员 或者 session失效(尝试一下退出重新登录)"
|
||||||
|
|
||||||
@@ -20,11 +17,11 @@ class ErrFormat(CommonErrFormat):
|
|||||||
role_exists = "角色 {} 已经存在!"
|
role_exists = "角色 {} 已经存在!"
|
||||||
global_role_not_found = "全局角色 {} 不存在!"
|
global_role_not_found = "全局角色 {} 不存在!"
|
||||||
global_role_exists = "全局角色 {} 已经存在!"
|
global_role_exists = "全局角色 {} 已经存在!"
|
||||||
|
user_role_delete_invalid = "删除用户角色, 请在 用户管理 页面操作!"
|
||||||
|
|
||||||
resource_no_permission = "您没有资源: {} 的 {} 权限"
|
resource_no_permission = "您没有资源: {} 的 {} 权限"
|
||||||
admin_required = "需要管理员权限"
|
admin_required = "需要管理员权限"
|
||||||
role_required = "需要角色: {}"
|
role_required = "需要角色: {}"
|
||||||
user_role_delete_invalid = "删除用户角色, 请在 用户管理 页面操作!"
|
|
||||||
|
|
||||||
app_is_ready_existed = "应用 {} 已经存在"
|
app_is_ready_existed = "应用 {} 已经存在"
|
||||||
app_not_found = "应用 {} 不存在!"
|
app_not_found = "应用 {} 不存在!"
|
||||||
|
@@ -41,7 +41,6 @@ class UserCRUD(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add(cls, **kwargs):
|
def add(cls, **kwargs):
|
||||||
add_from = kwargs.pop('add_from', None)
|
|
||||||
existed = User.get_by(username=kwargs['username'])
|
existed = User.get_by(username=kwargs['username'])
|
||||||
existed and abort(400, ErrFormat.user_exists.format(kwargs['username']))
|
existed and abort(400, ErrFormat.user_exists.format(kwargs['username']))
|
||||||
|
|
||||||
@@ -63,11 +62,10 @@ class UserCRUD(object):
|
|||||||
AuditCRUD.add_role_log(None, AuditOperateType.create,
|
AuditCRUD.add_role_log(None, AuditOperateType.create,
|
||||||
AuditScope.user, user.uid, {}, user.to_dict(), {}, {}
|
AuditScope.user, user.uid, {}, user.to_dict(), {}, {}
|
||||||
)
|
)
|
||||||
if add_from != 'common':
|
from api.lib.common_setting.employee import EmployeeCRUD
|
||||||
from api.lib.common_setting.employee import EmployeeCRUD
|
payload = {column: getattr(user, column) for column in ['uid', 'username', 'nickname', 'email', 'block']}
|
||||||
payload = {column: getattr(user, column) for column in ['uid', 'username', 'nickname', 'email', 'block']}
|
payload['rid'] = role.id
|
||||||
payload['rid'] = role.id
|
EmployeeCRUD.add_employee_from_acl_created(**payload)
|
||||||
EmployeeCRUD.add_employee_from_acl_created(**payload)
|
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
@@ -93,9 +93,6 @@ def _auth_with_token():
|
|||||||
|
|
||||||
|
|
||||||
def _auth_with_ip_white_list():
|
def _auth_with_ip_white_list():
|
||||||
if request.url.endswith("acl/users/info"):
|
|
||||||
return False
|
|
||||||
|
|
||||||
ip = request.headers.get('X-Real-IP') or request.remote_addr
|
ip = request.headers.get('X-Real-IP') or request.remote_addr
|
||||||
key = request.values.get('_key')
|
key = request.values.get('_key')
|
||||||
secret = request.values.get('_secret')
|
secret = request.values.get('_secret')
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
# -*- coding:utf-8 -*-
|
|
@@ -1,67 +0,0 @@
|
|||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from flask import abort
|
|
||||||
from flask import current_app
|
|
||||||
from flask import session
|
|
||||||
from ldap3 import ALL
|
|
||||||
from ldap3 import AUTO_BIND_NO_TLS
|
|
||||||
from ldap3 import Connection
|
|
||||||
from ldap3 import Server
|
|
||||||
from ldap3.core.exceptions import LDAPBindError
|
|
||||||
from ldap3.core.exceptions import LDAPCertificateError
|
|
||||||
from ldap3.core.exceptions import LDAPSocketOpenError
|
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.common_setting.const import AuthenticateType
|
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
from api.lib.perm.acl.resp_format import ErrFormat
|
|
||||||
from api.models.acl import User
|
|
||||||
|
|
||||||
|
|
||||||
def authenticate_with_ldap(username, password):
|
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.LDAP).get()
|
|
||||||
|
|
||||||
server = Server(config.get('LDAP').get('ldap_server'), get_info=ALL, connect_timeout=3)
|
|
||||||
if '@' in username:
|
|
||||||
email = username
|
|
||||||
who = config['LDAP'].get('ldap_user_dn').format(username.split('@')[0])
|
|
||||||
else:
|
|
||||||
who = config['LDAP'].get('ldap_user_dn').format(username)
|
|
||||||
email = "{}@{}".format(who, config['LDAP'].get('ldap_domain'))
|
|
||||||
|
|
||||||
username = username.split('@')[0]
|
|
||||||
user = User.query.get_by_username(username)
|
|
||||||
try:
|
|
||||||
if not password:
|
|
||||||
raise LDAPCertificateError
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = Connection(server, user=who, password=password, auto_bind=AUTO_BIND_NO_TLS)
|
|
||||||
except LDAPBindError:
|
|
||||||
conn = Connection(server,
|
|
||||||
user=f"{username}@{config['LDAP'].get('ldap_domain')}",
|
|
||||||
password=password,
|
|
||||||
auto_bind=AUTO_BIND_NO_TLS)
|
|
||||||
|
|
||||||
if conn.result['result'] != 0:
|
|
||||||
AuditCRUD.add_login_log(username, False, ErrFormat.invalid_password)
|
|
||||||
raise LDAPBindError
|
|
||||||
else:
|
|
||||||
_id = AuditCRUD.add_login_log(username, True, ErrFormat.login_succeed)
|
|
||||||
session['LOGIN_ID'] = _id
|
|
||||||
|
|
||||||
if not user:
|
|
||||||
from api.lib.perm.acl.user import UserCRUD
|
|
||||||
user = UserCRUD.add(username=username, email=email, password=uuid.uuid4().hex)
|
|
||||||
|
|
||||||
return user, True
|
|
||||||
|
|
||||||
except LDAPBindError as e:
|
|
||||||
current_app.logger.info(e)
|
|
||||||
return user, False
|
|
||||||
|
|
||||||
except LDAPSocketOpenError as e:
|
|
||||||
current_app.logger.info(e)
|
|
||||||
return abort(403, ErrFormat.ldap_connection_failed)
|
|
@@ -1,30 +0,0 @@
|
|||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from . import routing
|
|
||||||
|
|
||||||
|
|
||||||
class OAuth2(object):
|
|
||||||
def __init__(self, app=None, url_prefix=None):
|
|
||||||
self._app = app
|
|
||||||
if app is not None:
|
|
||||||
self.init_app(app, url_prefix)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def init_app(app, url_prefix=None):
|
|
||||||
# Configuration defaults
|
|
||||||
app.config.setdefault('OAUTH2_GRANT_TYPE', 'authorization_code')
|
|
||||||
app.config.setdefault('OAUTH2_RESPONSE_TYPE', 'code')
|
|
||||||
app.config.setdefault('OAUTH2_AFTER_LOGIN', '/')
|
|
||||||
|
|
||||||
app.config.setdefault('OIDC_GRANT_TYPE', 'authorization_code')
|
|
||||||
app.config.setdefault('OIDC_RESPONSE_TYPE', 'code')
|
|
||||||
app.config.setdefault('OIDC_AFTER_LOGIN', '/')
|
|
||||||
|
|
||||||
# Register Blueprint
|
|
||||||
app.register_blueprint(routing.blueprint, url_prefix=url_prefix)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def app(self):
|
|
||||||
return self._app or current_app
|
|
@@ -1,139 +0,0 @@
|
|||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import secrets
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import abort
|
|
||||||
from flask import current_app
|
|
||||||
from flask import redirect
|
|
||||||
from flask import request
|
|
||||||
from flask import session
|
|
||||||
from flask import url_for
|
|
||||||
from flask_login import login_user
|
|
||||||
from flask_login import logout_user
|
|
||||||
from six.moves.urllib.parse import urlencode
|
|
||||||
from six.moves.urllib.parse import urlparse
|
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
from api.lib.perm.acl.cache import UserCache
|
|
||||||
from api.lib.perm.acl.resp_format import ErrFormat
|
|
||||||
|
|
||||||
blueprint = Blueprint('oauth2', __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/<string:auth_type>/login')
|
|
||||||
def login(auth_type):
|
|
||||||
config = AuthenticateDataCRUD(auth_type.upper()).get()
|
|
||||||
|
|
||||||
if request.values.get("next"):
|
|
||||||
session["next"] = request.values.get("next")
|
|
||||||
|
|
||||||
session[f'{auth_type}_state'] = secrets.token_urlsafe(16)
|
|
||||||
|
|
||||||
auth_type = auth_type.upper()
|
|
||||||
|
|
||||||
redirect_uri = "{}://{}{}".format(urlparse(request.referrer).scheme,
|
|
||||||
urlparse(request.referrer).netloc,
|
|
||||||
url_for('oauth2.callback', auth_type=auth_type.lower()))
|
|
||||||
qs = urlencode({
|
|
||||||
'client_id': config['client_id'],
|
|
||||||
'redirect_uri': redirect_uri,
|
|
||||||
'response_type': current_app.config[f'{auth_type}_RESPONSE_TYPE'],
|
|
||||||
'scope': ' '.join(config['scopes'] or []),
|
|
||||||
'state': session[f'{auth_type.lower()}_state'],
|
|
||||||
})
|
|
||||||
|
|
||||||
return redirect("{}?{}".format(config['authorize_url'].split('?')[0], qs))
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/<string:auth_type>/callback')
|
|
||||||
def callback(auth_type):
|
|
||||||
auth_type = auth_type.upper()
|
|
||||||
config = AuthenticateDataCRUD(auth_type).get()
|
|
||||||
|
|
||||||
redirect_url = session.get("next") or config.get('after_login') or '/'
|
|
||||||
|
|
||||||
if request.values['state'] != session.get(f'{auth_type.lower()}_state'):
|
|
||||||
return abort(401, "state is invalid")
|
|
||||||
|
|
||||||
if 'code' not in request.values:
|
|
||||||
return abort(401, 'code is invalid')
|
|
||||||
|
|
||||||
response = requests.post(config['token_url'], data={
|
|
||||||
'client_id': config['client_id'],
|
|
||||||
'client_secret': config['client_secret'],
|
|
||||||
'code': request.values['code'],
|
|
||||||
'grant_type': current_app.config[f'{auth_type}_GRANT_TYPE'],
|
|
||||||
'redirect_uri': url_for('oauth2.callback', auth_type=auth_type.lower(), _external=True),
|
|
||||||
}, headers={'Accept': 'application/json'})
|
|
||||||
if response.status_code != 200:
|
|
||||||
current_app.logger.error(response.text)
|
|
||||||
return abort(401)
|
|
||||||
access_token = response.json().get('access_token')
|
|
||||||
if not access_token:
|
|
||||||
return abort(401)
|
|
||||||
|
|
||||||
response = requests.get(config['user_info']['url'], headers={
|
|
||||||
'Authorization': 'Bearer {}'.format(access_token),
|
|
||||||
'Accept': 'application/json',
|
|
||||||
})
|
|
||||||
if response.status_code != 200:
|
|
||||||
return abort(401)
|
|
||||||
|
|
||||||
res = response.json()
|
|
||||||
email = res.get(config['user_info']['email'])
|
|
||||||
username = res.get(config['user_info']['username'])
|
|
||||||
avatar = res.get(config['user_info'].get('avatar'))
|
|
||||||
user = UserCache.get(username)
|
|
||||||
if user is None:
|
|
||||||
current_app.logger.info("create user: {}".format(username))
|
|
||||||
from api.lib.perm.acl.user import UserCRUD
|
|
||||||
|
|
||||||
user_dict = dict(username=username, email=email, avatar=avatar)
|
|
||||||
user_dict['password'] = uuid.uuid4().hex
|
|
||||||
|
|
||||||
user = UserCRUD.add(**user_dict)
|
|
||||||
|
|
||||||
# log the user in
|
|
||||||
login_user(user)
|
|
||||||
|
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
|
||||||
user_info = ACLManager.get_user_info(username)
|
|
||||||
|
|
||||||
session["acl"] = dict(uid=user_info.get("uid"),
|
|
||||||
avatar=user.avatar if user else user_info.get("avatar"),
|
|
||||||
userId=user_info.get("uid"),
|
|
||||||
rid=user_info.get("rid"),
|
|
||||||
userName=user_info.get("username"),
|
|
||||||
nickName=user_info.get("nickname") or user_info.get("username"),
|
|
||||||
parentRoles=user_info.get("parents"),
|
|
||||||
childRoles=user_info.get("children"),
|
|
||||||
roleName=user_info.get("role"))
|
|
||||||
session["uid"] = user_info.get("uid")
|
|
||||||
|
|
||||||
_id = AuditCRUD.add_login_log(username, True, ErrFormat.login_succeed)
|
|
||||||
session['LOGIN_ID'] = _id
|
|
||||||
|
|
||||||
return redirect(redirect_url)
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/<string:auth_type>/logout')
|
|
||||||
def logout(auth_type):
|
|
||||||
"acl" in session and session.pop("acl")
|
|
||||||
"uid" in session and session.pop("uid")
|
|
||||||
f'{auth_type}_state' in session and session.pop(f'{auth_type}_state')
|
|
||||||
"next" in session and session.pop("next")
|
|
||||||
|
|
||||||
redirect_url = url_for('oauth2.login', auth_type=auth_type, _external=True, next=request.referrer)
|
|
||||||
|
|
||||||
logout_user()
|
|
||||||
|
|
||||||
current_app.logger.debug('Redirecting to: {0}'.format(redirect_url))
|
|
||||||
|
|
||||||
AuditCRUD.add_login_log(None, None, None, _id=session.get('LOGIN_ID'), logout_at=datetime.datetime.now())
|
|
||||||
|
|
||||||
return redirect(redirect_url)
|
|
@@ -38,6 +38,7 @@ def string_to_bytes(value):
|
|||||||
byte_string = value
|
byte_string = value
|
||||||
else:
|
else:
|
||||||
byte_string = value.encode("utf-8")
|
byte_string = value.encode("utf-8")
|
||||||
|
|
||||||
return byte_string
|
return byte_string
|
||||||
|
|
||||||
|
|
||||||
@@ -64,8 +65,7 @@ class KeyManage:
|
|||||||
self.backend = Backend(backend)
|
self.backend = Backend(backend)
|
||||||
|
|
||||||
def init_app(self, app, backend=None):
|
def init_app(self, app, backend=None):
|
||||||
if (sys.argv[0].endswith("gunicorn") or
|
if sys.argv[0].endswith("gunicorn") or (len(sys.argv) > 1 and sys.argv[1] == "run"):
|
||||||
(len(sys.argv) > 1 and sys.argv[1] in ("run", "cmdb-password-data-migrate"))):
|
|
||||||
self.trigger = app.config.get("INNER_TRIGGER_TOKEN")
|
self.trigger = app.config.get("INNER_TRIGGER_TOKEN")
|
||||||
if not self.trigger:
|
if not self.trigger:
|
||||||
return
|
return
|
||||||
@@ -313,7 +313,7 @@ class KeyManage:
|
|||||||
secrets_root_key = current_app.config.get("secrets_root_key")
|
secrets_root_key = current_app.config.get("secrets_root_key")
|
||||||
msg, ok = self.is_valid_root_key(secrets_root_key)
|
msg, ok = self.is_valid_root_key(secrets_root_key)
|
||||||
if not ok:
|
if not ok:
|
||||||
return true
|
return {"message": msg, "status": "failed"}
|
||||||
status = self.backend.get(backend_seal_key)
|
status = self.backend.get(backend_seal_key)
|
||||||
return status == "block"
|
return status == "block"
|
||||||
|
|
||||||
|
@@ -12,9 +12,6 @@ from Crypto.Cipher import AES
|
|||||||
from elasticsearch import Elasticsearch
|
from elasticsearch import Elasticsearch
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from api.lib.secrets.inner import InnerCrypt
|
|
||||||
from api.lib.secrets.inner import KeyManage
|
|
||||||
|
|
||||||
|
|
||||||
class BaseEnum(object):
|
class BaseEnum(object):
|
||||||
_ALL_ = set() # type: Set[str]
|
_ALL_ = set() # type: Set[str]
|
||||||
@@ -289,33 +286,3 @@ class AESCrypto(object):
|
|||||||
text_decrypted = cipher.decrypt(encode_bytes)
|
text_decrypted = cipher.decrypt(encode_bytes)
|
||||||
|
|
||||||
return cls.unpad(text_decrypted).decode('utf8')
|
return cls.unpad(text_decrypted).decode('utf8')
|
||||||
|
|
||||||
|
|
||||||
class Crypto(AESCrypto):
|
|
||||||
@classmethod
|
|
||||||
def encrypt(cls, data):
|
|
||||||
from api.lib.secrets.secrets import InnerKVManger
|
|
||||||
|
|
||||||
if not KeyManage(backend=InnerKVManger()).is_seal():
|
|
||||||
res, status = InnerCrypt().encrypt(data)
|
|
||||||
if status:
|
|
||||||
return res
|
|
||||||
|
|
||||||
return AESCrypto().encrypt(data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def decrypt(cls, data):
|
|
||||||
from api.lib.secrets.secrets import InnerKVManger
|
|
||||||
|
|
||||||
if not KeyManage(backend=InnerKVManger()).is_seal():
|
|
||||||
try:
|
|
||||||
res, status = InnerCrypt().decrypt(data)
|
|
||||||
if status:
|
|
||||||
return res
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
return AESCrypto().decrypt(data)
|
|
||||||
except:
|
|
||||||
return data
|
|
||||||
|
@@ -5,18 +5,17 @@ import copy
|
|||||||
import hashlib
|
import hashlib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from ldap3 import Server, Connection, ALL
|
||||||
|
from ldap3.core.exceptions import LDAPBindError, LDAPCertificateError
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import session
|
|
||||||
from flask_sqlalchemy import BaseQuery
|
from flask_sqlalchemy import BaseQuery
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.database import CRUDModel
|
from api.lib.database import CRUDModel
|
||||||
from api.lib.database import Model
|
from api.lib.database import Model
|
||||||
from api.lib.database import Model2
|
|
||||||
from api.lib.database import SoftDeleteMixin
|
from api.lib.database import SoftDeleteMixin
|
||||||
from api.lib.perm.acl.const import ACL_QUEUE
|
from api.lib.perm.acl.const import ACL_QUEUE
|
||||||
from api.lib.perm.acl.const import OperateType
|
from api.lib.perm.acl.const import OperateType
|
||||||
from api.lib.perm.acl.resp_format import ErrFormat
|
|
||||||
|
|
||||||
|
|
||||||
class App(Model):
|
class App(Model):
|
||||||
@@ -29,26 +28,21 @@ class App(Model):
|
|||||||
|
|
||||||
|
|
||||||
class UserQuery(BaseQuery):
|
class UserQuery(BaseQuery):
|
||||||
|
def _join(self, *args, **kwargs):
|
||||||
|
super(UserQuery, self)._join(*args, **kwargs)
|
||||||
|
|
||||||
def authenticate(self, login, password):
|
def authenticate(self, login, password):
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
|
|
||||||
user = self.filter(db.or_(User.username == login,
|
user = self.filter(db.or_(User.username == login,
|
||||||
User.email == login)).filter(User.deleted.is_(False)).filter(User.block == 0).first()
|
User.email == login)).filter(User.deleted.is_(False)).filter(User.block == 0).first()
|
||||||
if user:
|
if user:
|
||||||
|
current_app.logger.info(user)
|
||||||
authenticated = user.check_password(password)
|
authenticated = user.check_password(password)
|
||||||
if authenticated:
|
if authenticated:
|
||||||
_id = AuditCRUD.add_login_log(login, True, ErrFormat.login_succeed)
|
from api.tasks.acl import op_record
|
||||||
session['LOGIN_ID'] = _id
|
op_record.apply_async(args=(None, login, OperateType.LOGIN, ["ACL"]), queue=ACL_QUEUE)
|
||||||
else:
|
|
||||||
AuditCRUD.add_login_log(login, False, ErrFormat.invalid_password)
|
|
||||||
else:
|
else:
|
||||||
authenticated = False
|
authenticated = False
|
||||||
|
|
||||||
AuditCRUD.add_login_log(login, False, ErrFormat.user_not_found.format(login))
|
|
||||||
|
|
||||||
current_app.logger.info(("login", login, user, authenticated))
|
|
||||||
|
|
||||||
return user, authenticated
|
return user, authenticated
|
||||||
|
|
||||||
def authenticate_with_key(self, key, secret, args, path):
|
def authenticate_with_key(self, key, secret, args, path):
|
||||||
@@ -63,6 +57,38 @@ class UserQuery(BaseQuery):
|
|||||||
|
|
||||||
return user, authenticated
|
return user, authenticated
|
||||||
|
|
||||||
|
def authenticate_with_ldap(self, username, password):
|
||||||
|
server = Server(current_app.config.get('LDAP_SERVER'), get_info=ALL)
|
||||||
|
if '@' in username:
|
||||||
|
email = username
|
||||||
|
who = current_app.config.get('LDAP_USER_DN').format(username.split('@')[0])
|
||||||
|
else:
|
||||||
|
who = current_app.config.get('LDAP_USER_DN').format(username)
|
||||||
|
email = "{}@{}".format(who, current_app.config.get('LDAP_DOMAIN'))
|
||||||
|
|
||||||
|
username = username.split('@')[0]
|
||||||
|
user = self.get_by_username(username)
|
||||||
|
try:
|
||||||
|
if not password:
|
||||||
|
raise LDAPCertificateError
|
||||||
|
|
||||||
|
conn = Connection(server, user=who, password=password)
|
||||||
|
conn.bind()
|
||||||
|
if conn.result['result'] != 0:
|
||||||
|
raise LDAPBindError
|
||||||
|
conn.unbind()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
from api.lib.perm.acl.user import UserCRUD
|
||||||
|
user = UserCRUD.add(username=username, email=email)
|
||||||
|
|
||||||
|
from api.tasks.acl import op_record
|
||||||
|
op_record.apply_async(args=(None, username, OperateType.LOGIN, ["ACL"]), queue=ACL_QUEUE)
|
||||||
|
|
||||||
|
return user, True
|
||||||
|
except LDAPBindError:
|
||||||
|
return user, False
|
||||||
|
|
||||||
def search(self, key):
|
def search(self, key):
|
||||||
query = self.filter(db.or_(User.email == key,
|
query = self.filter(db.or_(User.email == key,
|
||||||
User.nickname.ilike('%' + key + '%'),
|
User.nickname.ilike('%' + key + '%'),
|
||||||
@@ -112,7 +138,6 @@ class User(CRUDModel, SoftDeleteMixin):
|
|||||||
wx_id = db.Column(db.String(32))
|
wx_id = db.Column(db.String(32))
|
||||||
employee_id = db.Column(db.String(16), index=True)
|
employee_id = db.Column(db.String(16), index=True)
|
||||||
avatar = db.Column(db.String(128))
|
avatar = db.Column(db.String(128))
|
||||||
|
|
||||||
# apps = db.Column(db.JSON)
|
# apps = db.Column(db.JSON)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -143,6 +168,8 @@ class User(CRUDModel, SoftDeleteMixin):
|
|||||||
|
|
||||||
|
|
||||||
class RoleQuery(BaseQuery):
|
class RoleQuery(BaseQuery):
|
||||||
|
def _join(self, *args, **kwargs):
|
||||||
|
super(RoleQuery, self)._join(*args, **kwargs)
|
||||||
|
|
||||||
def authenticate(self, login, password):
|
def authenticate(self, login, password):
|
||||||
role = self.filter(Role.name == login).first()
|
role = self.filter(Role.name == login).first()
|
||||||
@@ -350,16 +377,3 @@ class AuditTriggerLog(Model):
|
|||||||
current = db.Column(db.JSON, default=dict(), comment='当前数据')
|
current = db.Column(db.JSON, default=dict(), comment='当前数据')
|
||||||
extra = db.Column(db.JSON, default=dict(), comment='权限名')
|
extra = db.Column(db.JSON, default=dict(), comment='权限名')
|
||||||
source = db.Column(db.String(16), default='', comment='来源')
|
source = db.Column(db.String(16), default='', comment='来源')
|
||||||
|
|
||||||
|
|
||||||
class AuditLoginLog(Model2):
|
|
||||||
__tablename__ = "acl_audit_login_logs"
|
|
||||||
|
|
||||||
username = db.Column(db.String(64), index=True)
|
|
||||||
channel = db.Column(db.Enum('web', 'api'), default="web")
|
|
||||||
ip = db.Column(db.String(15))
|
|
||||||
browser = db.Column(db.String(256))
|
|
||||||
description = db.Column(db.String(128))
|
|
||||||
is_ok = db.Column(db.Boolean)
|
|
||||||
login_at = db.Column(db.DateTime)
|
|
||||||
logout_at = db.Column(db.DateTime)
|
|
||||||
|
@@ -12,9 +12,7 @@ from api.lib.cmdb.const import CITypeOperateType
|
|||||||
from api.lib.cmdb.const import ConstraintEnum
|
from api.lib.cmdb.const import ConstraintEnum
|
||||||
from api.lib.cmdb.const import OperateType
|
from api.lib.cmdb.const import OperateType
|
||||||
from api.lib.cmdb.const import ValueTypeEnum
|
from api.lib.cmdb.const import ValueTypeEnum
|
||||||
from api.lib.database import Model
|
from api.lib.database import Model, Model2
|
||||||
from api.lib.database import Model2
|
|
||||||
from api.lib.utils import Crypto
|
|
||||||
|
|
||||||
|
|
||||||
# template
|
# template
|
||||||
@@ -91,37 +89,13 @@ class Attribute(Model):
|
|||||||
compute_expr = db.Column(db.Text)
|
compute_expr = db.Column(db.Text)
|
||||||
compute_script = db.Column(db.Text)
|
compute_script = db.Column(db.Text)
|
||||||
|
|
||||||
_choice_web_hook = db.Column('choice_web_hook', db.JSON)
|
choice_web_hook = db.Column(db.JSON)
|
||||||
choice_other = db.Column(db.JSON)
|
choice_other = db.Column(db.JSON)
|
||||||
|
|
||||||
uid = db.Column(db.Integer, index=True)
|
uid = db.Column(db.Integer, index=True)
|
||||||
|
|
||||||
option = db.Column(db.JSON)
|
option = db.Column(db.JSON)
|
||||||
|
|
||||||
def _get_webhook(self):
|
|
||||||
if self._choice_web_hook:
|
|
||||||
if self._choice_web_hook.get('headers') and "Cookie" in self._choice_web_hook['headers']:
|
|
||||||
self._choice_web_hook['headers']['Cookie'] = Crypto.decrypt(self._choice_web_hook['headers']['Cookie'])
|
|
||||||
|
|
||||||
if self._choice_web_hook.get('authorization'):
|
|
||||||
for k, v in self._choice_web_hook['authorization'].items():
|
|
||||||
self._choice_web_hook['authorization'][k] = Crypto.decrypt(v)
|
|
||||||
|
|
||||||
return self._choice_web_hook
|
|
||||||
|
|
||||||
def _set_webhook(self, data):
|
|
||||||
if data:
|
|
||||||
if data.get('headers') and "Cookie" in data['headers']:
|
|
||||||
data['headers']['Cookie'] = Crypto.encrypt(data['headers']['Cookie'])
|
|
||||||
|
|
||||||
if data.get('authorization'):
|
|
||||||
for k, v in data['authorization'].items():
|
|
||||||
data['authorization'][k] = Crypto.encrypt(v)
|
|
||||||
|
|
||||||
self._choice_web_hook = data
|
|
||||||
|
|
||||||
choice_web_hook = db.synonym("_choice_web_hook", descriptor=property(_get_webhook, _set_webhook))
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeAttribute(Model):
|
class CITypeAttribute(Model):
|
||||||
__tablename__ = "c_ci_type_attributes"
|
__tablename__ = "c_ci_type_attributes"
|
||||||
@@ -156,25 +130,7 @@ class CITypeTrigger(Model):
|
|||||||
|
|
||||||
type_id = db.Column(db.Integer, db.ForeignKey('c_ci_types.id'), nullable=False)
|
type_id = db.Column(db.Integer, db.ForeignKey('c_ci_types.id'), nullable=False)
|
||||||
attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"))
|
attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"))
|
||||||
_option = db.Column('notify', db.JSON)
|
option = db.Column('notify', db.JSON)
|
||||||
|
|
||||||
def _get_option(self):
|
|
||||||
if self._option and self._option.get('webhooks'):
|
|
||||||
if self._option['webhooks'].get('authorization'):
|
|
||||||
for k, v in self._option['webhooks']['authorization'].items():
|
|
||||||
self._option['webhooks']['authorization'][k] = Crypto.decrypt(v)
|
|
||||||
|
|
||||||
return self._option
|
|
||||||
|
|
||||||
def _set_option(self, data):
|
|
||||||
if data and data.get('webhooks'):
|
|
||||||
if data['webhooks'].get('authorization'):
|
|
||||||
for k, v in data['webhooks']['authorization'].items():
|
|
||||||
data['webhooks']['authorization'][k] = Crypto.encrypt(v)
|
|
||||||
|
|
||||||
self._option = data
|
|
||||||
|
|
||||||
option = db.synonym("_option", descriptor=property(_get_option, _set_option))
|
|
||||||
|
|
||||||
|
|
||||||
class CITriggerHistory(Model):
|
class CITriggerHistory(Model):
|
||||||
@@ -218,8 +174,6 @@ class CIRelation(Model):
|
|||||||
relation_type_id = db.Column(db.Integer, db.ForeignKey("c_relation_types.id"), nullable=False)
|
relation_type_id = db.Column(db.Integer, db.ForeignKey("c_relation_types.id"), nullable=False)
|
||||||
more = db.Column(db.Integer, db.ForeignKey("c_cis.id"))
|
more = db.Column(db.Integer, db.ForeignKey("c_cis.id"))
|
||||||
|
|
||||||
ancestor_ids = db.Column(db.String(128), index=True)
|
|
||||||
|
|
||||||
first_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.first_ci_id")
|
first_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.first_ci_id")
|
||||||
second_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.second_ci_id")
|
second_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.second_ci_id")
|
||||||
relation_type = db.relationship("RelationType", backref="c_ci_relations.relation_type_id")
|
relation_type = db.relationship("RelationType", backref="c_ci_relations.relation_type_id")
|
||||||
|
@@ -96,11 +96,3 @@ class NoticeConfig(Model):
|
|||||||
|
|
||||||
platform = db.Column(db.VARCHAR(255), nullable=False)
|
platform = db.Column(db.VARCHAR(255), nullable=False)
|
||||||
info = db.Column(db.JSON)
|
info = db.Column(db.JSON)
|
||||||
|
|
||||||
|
|
||||||
class CommonFile(Model):
|
|
||||||
__tablename__ = 'common_file'
|
|
||||||
|
|
||||||
file_name = db.Column(db.VARCHAR(512), nullable=False, index=True)
|
|
||||||
origin_name = db.Column(db.VARCHAR(512), nullable=False)
|
|
||||||
binary = db.Column(db.LargeBinary(16777216), nullable=False)
|
|
||||||
|
@@ -16,7 +16,6 @@ from api.lib.cmdb.cache import CITypeAttributesCache
|
|||||||
from api.lib.cmdb.const import CMDB_QUEUE
|
from api.lib.cmdb.const import CMDB_QUEUE
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
|
|
||||||
from api.lib.decorator import flush_db
|
from api.lib.decorator import flush_db
|
||||||
from api.lib.decorator import reconnect_db
|
from api.lib.decorator import reconnect_db
|
||||||
from api.lib.perm.acl.cache import UserCache
|
from api.lib.perm.acl.cache import UserCache
|
||||||
@@ -98,30 +97,16 @@ def ci_delete_trigger(trigger, operate_type, ci_dict):
|
|||||||
@celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
|
||||||
@flush_db
|
@flush_db
|
||||||
@reconnect_db
|
@reconnect_db
|
||||||
def ci_relation_cache(parent_id, child_id, ancestor_ids):
|
def ci_relation_cache(parent_id, child_id):
|
||||||
with Lock("CIRelation_{}".format(parent_id)):
|
with Lock("CIRelation_{}".format(parent_id)):
|
||||||
if ancestor_ids is None:
|
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
||||||
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
children = json.loads(children) if children is not None else {}
|
||||||
children = json.loads(children) if children is not None else {}
|
|
||||||
|
|
||||||
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, ancestor_ids=ancestor_ids,
|
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, first=True, to_dict=False)
|
||||||
first=True, to_dict=False)
|
if str(child_id) not in children:
|
||||||
if str(child_id) not in children:
|
children[str(child_id)] = cr.second_ci.type_id
|
||||||
children[str(child_id)] = cr.second_ci.type_id
|
|
||||||
|
|
||||||
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
|
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
|
||||||
|
|
||||||
else:
|
|
||||||
key = "{},{}".format(ancestor_ids, parent_id)
|
|
||||||
grandson = rd.get([key], REDIS_PREFIX_CI_RELATION2)[0]
|
|
||||||
grandson = json.loads(grandson) if grandson is not None else {}
|
|
||||||
|
|
||||||
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, ancestor_ids=ancestor_ids,
|
|
||||||
first=True, to_dict=False)
|
|
||||||
if cr and str(cr.second_ci_id) not in grandson:
|
|
||||||
grandson[str(cr.second_ci_id)] = cr.second_ci.type_id
|
|
||||||
|
|
||||||
rd.create_or_update({key: json.dumps(grandson)}, REDIS_PREFIX_CI_RELATION2)
|
|
||||||
|
|
||||||
current_app.logger.info("ADD ci relation cache: {0} -> {1}".format(parent_id, child_id))
|
current_app.logger.info("ADD ci relation cache: {0} -> {1}".format(parent_id, child_id))
|
||||||
|
|
||||||
@@ -171,31 +156,20 @@ def ci_relation_add(parent_dict, child_id, uid):
|
|||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except:
|
except:
|
||||||
db.session.rollback()
|
pass
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
|
||||||
@reconnect_db
|
@reconnect_db
|
||||||
def ci_relation_delete(parent_id, child_id, ancestor_ids):
|
def ci_relation_delete(parent_id, child_id):
|
||||||
with Lock("CIRelation_{}".format(parent_id)):
|
with Lock("CIRelation_{}".format(parent_id)):
|
||||||
if ancestor_ids is None:
|
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
||||||
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
children = json.loads(children) if children is not None else {}
|
||||||
children = json.loads(children) if children is not None else {}
|
|
||||||
|
|
||||||
if str(child_id) in children:
|
if str(child_id) in children:
|
||||||
children.pop(str(child_id))
|
children.pop(str(child_id))
|
||||||
|
|
||||||
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
|
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
|
||||||
|
|
||||||
else:
|
|
||||||
key = "{},{}".format(ancestor_ids, parent_id)
|
|
||||||
grandson = rd.get([key], REDIS_PREFIX_CI_RELATION2)[0]
|
|
||||||
grandson = json.loads(grandson) if grandson is not None else {}
|
|
||||||
|
|
||||||
if str(child_id) in grandson:
|
|
||||||
grandson.pop(str(child_id))
|
|
||||||
|
|
||||||
rd.create_or_update({key: json.dumps(grandson)}, REDIS_PREFIX_CI_RELATION2)
|
|
||||||
|
|
||||||
current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id))
|
current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id))
|
||||||
|
|
||||||
|
@@ -24,7 +24,6 @@ class AuditLogView(APIView):
|
|||||||
'role': AuditCRUD.search_role,
|
'role': AuditCRUD.search_role,
|
||||||
'trigger': AuditCRUD.search_trigger,
|
'trigger': AuditCRUD.search_trigger,
|
||||||
'resource': AuditCRUD.search_resource,
|
'resource': AuditCRUD.search_resource,
|
||||||
'login': AuditCRUD.search_login,
|
|
||||||
}
|
}
|
||||||
if name not in func_map:
|
if name not in func_map:
|
||||||
abort(400, f'wrong {name}, please use {func_map.keys()}')
|
abort(400, f'wrong {name}, please use {func_map.keys()}')
|
||||||
|
@@ -8,15 +8,11 @@ from flask import abort
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask import session
|
from flask import session
|
||||||
from flask_login import login_user
|
from flask_login import login_user, logout_user
|
||||||
from flask_login import logout_user
|
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.common_setting.const import AuthenticateType
|
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
from api.lib.decorator import args_validate
|
from api.lib.decorator import args_validate
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
from api.lib.perm.acl.acl import ACLManager
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
from api.lib.perm.acl.cache import RoleCache
|
from api.lib.perm.acl.cache import RoleCache
|
||||||
from api.lib.perm.acl.cache import User
|
from api.lib.perm.acl.cache import User
|
||||||
from api.lib.perm.acl.cache import UserCache
|
from api.lib.perm.acl.cache import UserCache
|
||||||
@@ -38,10 +34,8 @@ class LoginView(APIView):
|
|||||||
username = request.values.get("username") or request.values.get("email")
|
username = request.values.get("username") or request.values.get("email")
|
||||||
password = request.values.get("password")
|
password = request.values.get("password")
|
||||||
_role = None
|
_role = None
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.LDAP).get()
|
if current_app.config.get('AUTH_WITH_LDAP'):
|
||||||
if config.get('LDAP', {}).get('enabled') or config.get('LDAP', {}).get('enable'):
|
user, authenticated = User.query.authenticate_with_ldap(username, password)
|
||||||
from api.lib.perm.authentication.ldap import authenticate_with_ldap
|
|
||||||
user, authenticated = authenticate_with_ldap(username, password)
|
|
||||||
else:
|
else:
|
||||||
user, authenticated = User.query.authenticate(username, password)
|
user, authenticated = User.query.authenticate(username, password)
|
||||||
if not user:
|
if not user:
|
||||||
@@ -182,7 +176,4 @@ class LogoutView(APIView):
|
|||||||
@auth_abandoned
|
@auth_abandoned
|
||||||
def post(self):
|
def post(self):
|
||||||
logout_user()
|
logout_user()
|
||||||
|
|
||||||
AuditCRUD.add_login_log(None, None, None, _id=session.get('LOGIN_ID'), logout_at=datetime.datetime.now())
|
|
||||||
|
|
||||||
self.jsonify(code=200)
|
self.jsonify(code=200)
|
||||||
|
@@ -35,7 +35,6 @@ class CIRelationSearchView(APIView):
|
|||||||
count = get_page_size(request.values.get("count") or request.values.get("page_size"))
|
count = get_page_size(request.values.get("count") or request.values.get("page_size"))
|
||||||
|
|
||||||
root_id = request.values.get('root_id')
|
root_id = request.values.get('root_id')
|
||||||
ancestor_ids = request.values.get('ancestor_ids') or None # only for many to many
|
|
||||||
level = list(map(int, handle_arg_list(request.values.get('level', '1'))))
|
level = list(map(int, handle_arg_list(request.values.get('level', '1'))))
|
||||||
|
|
||||||
query = request.values.get('q', "")
|
query = request.values.get('q', "")
|
||||||
@@ -45,7 +44,7 @@ class CIRelationSearchView(APIView):
|
|||||||
reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE')
|
reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE')
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
s = Search(root_id, level, query, fl, facet, page, count, sort, reverse, ancestor_ids=ancestor_ids)
|
s = Search(root_id, level, query, fl, facet, page, count, sort, reverse)
|
||||||
try:
|
try:
|
||||||
response, counter, total, page, numfound, facet = s.search()
|
response, counter, total, page, numfound, facet = s.search()
|
||||||
except SearchError as e:
|
except SearchError as e:
|
||||||
@@ -68,10 +67,9 @@ class CIRelationStatisticsView(APIView):
|
|||||||
root_ids = list(map(int, handle_arg_list(request.values.get('root_ids'))))
|
root_ids = list(map(int, handle_arg_list(request.values.get('root_ids'))))
|
||||||
level = request.values.get('level', 1)
|
level = request.values.get('level', 1)
|
||||||
type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', []))))
|
type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', []))))
|
||||||
ancestor_ids = request.values.get('ancestor_ids') or None # only for many to many
|
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
s = Search(root_ids, level, ancestor_ids=ancestor_ids)
|
s = Search(root_ids, level)
|
||||||
try:
|
try:
|
||||||
result = s.statistics(type_ids)
|
result = s.statistics(type_ids)
|
||||||
except SearchError as e:
|
except SearchError as e:
|
||||||
@@ -123,18 +121,14 @@ class CIRelationView(APIView):
|
|||||||
url_prefix = "/ci_relations/<int:first_ci_id>/<int:second_ci_id>"
|
url_prefix = "/ci_relations/<int:first_ci_id>/<int:second_ci_id>"
|
||||||
|
|
||||||
def post(self, first_ci_id, second_ci_id):
|
def post(self, first_ci_id, second_ci_id):
|
||||||
ancestor_ids = request.values.get('ancestor_ids') or None
|
|
||||||
|
|
||||||
manager = CIRelationManager()
|
manager = CIRelationManager()
|
||||||
res = manager.add(first_ci_id, second_ci_id, ancestor_ids=ancestor_ids)
|
res = manager.add(first_ci_id, second_ci_id)
|
||||||
|
|
||||||
return self.jsonify(cr_id=res)
|
return self.jsonify(cr_id=res)
|
||||||
|
|
||||||
def delete(self, first_ci_id, second_ci_id):
|
def delete(self, first_ci_id, second_ci_id):
|
||||||
ancestor_ids = request.values.get('ancestor_ids') or None
|
|
||||||
|
|
||||||
manager = CIRelationManager()
|
manager = CIRelationManager()
|
||||||
manager.delete_2(first_ci_id, second_ci_id, ancestor_ids=ancestor_ids)
|
manager.delete_2(first_ci_id, second_ci_id)
|
||||||
|
|
||||||
return self.jsonify(message="CIType Relation is deleted")
|
return self.jsonify(message="CIType Relation is deleted")
|
||||||
|
|
||||||
@@ -157,9 +151,8 @@ class BatchCreateOrUpdateCIRelationView(APIView):
|
|||||||
ci_ids = list(map(int, request.values.get('ci_ids')))
|
ci_ids = list(map(int, request.values.get('ci_ids')))
|
||||||
parents = list(map(int, request.values.get('parents', [])))
|
parents = list(map(int, request.values.get('parents', [])))
|
||||||
children = list(map(int, request.values.get('children', [])))
|
children = list(map(int, request.values.get('children', [])))
|
||||||
ancestor_ids = request.values.get('ancestor_ids') or None
|
|
||||||
|
|
||||||
CIRelationManager.batch_update(ci_ids, parents, children, ancestor_ids=ancestor_ids)
|
CIRelationManager.batch_update(ci_ids, parents, children)
|
||||||
|
|
||||||
return self.jsonify(code=200)
|
return self.jsonify(code=200)
|
||||||
|
|
||||||
@@ -173,8 +166,7 @@ class BatchCreateOrUpdateCIRelationView(APIView):
|
|||||||
def delete(self):
|
def delete(self):
|
||||||
ci_ids = list(map(int, request.values.get('ci_ids')))
|
ci_ids = list(map(int, request.values.get('ci_ids')))
|
||||||
parents = list(map(int, request.values.get('parents', [])))
|
parents = list(map(int, request.values.get('parents', [])))
|
||||||
ancestor_ids = request.values.get('ancestor_ids') or None
|
|
||||||
|
|
||||||
CIRelationManager.batch_delete(ci_ids, parents, ancestor_ids=ancestor_ids)
|
CIRelationManager.batch_delete(ci_ids, parents)
|
||||||
|
|
||||||
return self.jsonify(code=200)
|
return self.jsonify(code=200)
|
||||||
|
@@ -105,7 +105,6 @@ class CITypeGroupView(APIView):
|
|||||||
|
|
||||||
return self.jsonify(group.to_dict())
|
return self.jsonify(group.to_dict())
|
||||||
|
|
||||||
@role_required(RoleEnum.CONFIG)
|
|
||||||
@args_validate(CITypeGroupManager.cls)
|
@args_validate(CITypeGroupManager.cls)
|
||||||
def put(self, gid=None):
|
def put(self, gid=None):
|
||||||
if "/order" in request.url:
|
if "/order" in request.url:
|
||||||
@@ -507,4 +506,3 @@ class CITypeFilterPermissionView(APIView):
|
|||||||
@auth_with_app_token
|
@auth_with_app_token
|
||||||
def get(self, type_id):
|
def get(self, type_id):
|
||||||
return self.jsonify(CIFilterPermsCRUD().get(type_id))
|
return self.jsonify(CIFilterPermsCRUD().get(type_id))
|
||||||
|
|
||||||
|
@@ -9,7 +9,6 @@ from api.lib.cmdb.ci_type import CITypeRelationManager
|
|||||||
from api.lib.cmdb.const import PermEnum
|
from api.lib.cmdb.const import PermEnum
|
||||||
from api.lib.cmdb.const import ResourceTypeEnum
|
from api.lib.cmdb.const import ResourceTypeEnum
|
||||||
from api.lib.cmdb.const import RoleEnum
|
from api.lib.cmdb.const import RoleEnum
|
||||||
from api.lib.cmdb.preference import PreferenceManager
|
|
||||||
from api.lib.cmdb.resp_format import ErrFormat
|
from api.lib.cmdb.resp_format import ErrFormat
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
from api.lib.perm.acl.acl import ACLManager
|
||||||
@@ -110,10 +109,3 @@ class CITypeRelationRevokeView(APIView):
|
|||||||
acl.revoke_resource_from_role_by_rid(resource_name, rid, ResourceTypeEnum.CI_TYPE_RELATION, perms)
|
acl.revoke_resource_from_role_by_rid(resource_name, rid, ResourceTypeEnum.CI_TYPE_RELATION, perms)
|
||||||
|
|
||||||
return self.jsonify(code=200)
|
return self.jsonify(code=200)
|
||||||
|
|
||||||
|
|
||||||
class CITypeRelationCanEditView(APIView):
|
|
||||||
url_prefix = "/ci_type_relations/<int:parent_id>/<int:child_id>/can_edit"
|
|
||||||
|
|
||||||
def get(self, parent_id, child_id):
|
|
||||||
return self.jsonify(result=PreferenceManager.can_edit_relation(parent_id, child_id))
|
|
||||||
|
@@ -1,88 +0,0 @@
|
|||||||
from flask import abort, request
|
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.common_setting.const import TestType
|
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
|
||||||
from api.lib.perm.acl.acl import role_required
|
|
||||||
from api.resource import APIView
|
|
||||||
|
|
||||||
prefix = '/auth_config'
|
|
||||||
|
|
||||||
|
|
||||||
class AuthConfigView(APIView):
|
|
||||||
url_prefix = (f'{prefix}/<string:auth_type>',)
|
|
||||||
|
|
||||||
@role_required("acl_admin")
|
|
||||||
def get(self, auth_type):
|
|
||||||
cli = AuthenticateDataCRUD(auth_type)
|
|
||||||
|
|
||||||
if auth_type not in cli.get_support_type_list():
|
|
||||||
abort(400, ErrFormat.not_support_auth_type.format(auth_type))
|
|
||||||
|
|
||||||
if auth_type in cli.common_type_list:
|
|
||||||
data = cli.get_record(True)
|
|
||||||
else:
|
|
||||||
data = cli.get_record_with_decrypt()
|
|
||||||
return self.jsonify(data)
|
|
||||||
|
|
||||||
@role_required("acl_admin")
|
|
||||||
def post(self, auth_type):
|
|
||||||
cli = AuthenticateDataCRUD(auth_type)
|
|
||||||
|
|
||||||
if auth_type not in cli.get_support_type_list():
|
|
||||||
abort(400, ErrFormat.not_support_auth_type.format(auth_type))
|
|
||||||
|
|
||||||
params = request.json
|
|
||||||
data = params.get('data', {})
|
|
||||||
if auth_type in cli.common_type_list:
|
|
||||||
data['encrypt'] = False
|
|
||||||
cli.create(data)
|
|
||||||
|
|
||||||
return self.jsonify(params)
|
|
||||||
|
|
||||||
|
|
||||||
class AuthConfigViewWithId(APIView):
|
|
||||||
url_prefix = (f'{prefix}/<string:auth_type>/<int:_id>',)
|
|
||||||
|
|
||||||
@role_required("acl_admin")
|
|
||||||
def put(self, auth_type, _id):
|
|
||||||
cli = AuthenticateDataCRUD(auth_type)
|
|
||||||
|
|
||||||
if auth_type not in cli.get_support_type_list():
|
|
||||||
abort(400, ErrFormat.not_support_auth_type.format(auth_type))
|
|
||||||
|
|
||||||
params = request.json
|
|
||||||
data = params.get('data', {})
|
|
||||||
if auth_type in cli.common_type_list:
|
|
||||||
data['encrypt'] = False
|
|
||||||
|
|
||||||
res = cli.update(_id, data)
|
|
||||||
|
|
||||||
return self.jsonify(res.to_dict())
|
|
||||||
|
|
||||||
@role_required("acl_admin")
|
|
||||||
def delete(self, auth_type, _id):
|
|
||||||
cli = AuthenticateDataCRUD(auth_type)
|
|
||||||
|
|
||||||
if auth_type not in cli.get_support_type_list():
|
|
||||||
abort(400, ErrFormat.not_support_auth_type.format(auth_type))
|
|
||||||
cli.delete(_id)
|
|
||||||
return self.jsonify({})
|
|
||||||
|
|
||||||
|
|
||||||
class AuthEnableListView(APIView):
|
|
||||||
url_prefix = (f'{prefix}/enable_list',)
|
|
||||||
|
|
||||||
method_decorators = []
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
return self.jsonify(AuthenticateDataCRUD.get_enable_list())
|
|
||||||
|
|
||||||
|
|
||||||
class AuthConfigTestView(APIView):
|
|
||||||
url_prefix = (f'{prefix}/<string:auth_type>/test',)
|
|
||||||
|
|
||||||
def post(self, auth_type):
|
|
||||||
test_type = request.values.get('test_type', TestType.Connect)
|
|
||||||
params = request.json
|
|
||||||
return self.jsonify(AuthenticateDataCRUD(auth_type).test(test_type, params.get('data')))
|
|
@@ -1,7 +1,9 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
from flask import abort
|
||||||
from flask import request
|
from flask import request
|
||||||
|
|
||||||
from api.lib.common_setting.company_info import CompanyInfoCRUD
|
from api.lib.common_setting.company_info import CompanyInfoCRUD
|
||||||
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
from api.resource import APIView
|
from api.resource import APIView
|
||||||
|
|
||||||
prefix = '/company'
|
prefix = '/company'
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
from flask import abort
|
import os
|
||||||
|
|
||||||
|
from flask import abort, current_app, send_from_directory
|
||||||
from flask import request
|
from flask import request
|
||||||
from werkzeug.datastructures import MultiDict
|
from werkzeug.datastructures import MultiDict
|
||||||
|
|
||||||
|
@@ -3,10 +3,9 @@ import os
|
|||||||
|
|
||||||
from flask import request, abort, current_app, send_from_directory
|
from flask import request, abort, current_app, send_from_directory
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
import lz4.frame
|
|
||||||
|
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
from api.lib.common_setting.upload_file import allowed_file, generate_new_file_name, CommonFileCRUD
|
from api.lib.common_setting.upload_file import allowed_file, generate_new_file_name
|
||||||
from api.resource import APIView
|
from api.resource import APIView
|
||||||
|
|
||||||
prefix = '/file'
|
prefix = '/file'
|
||||||
@@ -29,8 +28,7 @@ class GetFileView(APIView):
|
|||||||
url_prefix = (f'{prefix}/<string:_filename>',)
|
url_prefix = (f'{prefix}/<string:_filename>',)
|
||||||
|
|
||||||
def get(self, _filename):
|
def get(self, _filename):
|
||||||
file_stream = CommonFileCRUD.get_file(_filename)
|
return send_from_directory(current_app.config['UPLOAD_DIRECTORY_FULL'], _filename, as_attachment=True)
|
||||||
return self.send_file(file_stream, as_attachment=True, download_name=_filename)
|
|
||||||
|
|
||||||
|
|
||||||
class PostFileView(APIView):
|
class PostFileView(APIView):
|
||||||
@@ -55,20 +53,11 @@ class PostFileView(APIView):
|
|||||||
filename = file.filename
|
filename = file.filename
|
||||||
|
|
||||||
if allowed_file(filename, current_app.config.get('ALLOWED_EXTENSIONS', ALLOWED_EXTENSIONS)):
|
if allowed_file(filename, current_app.config.get('ALLOWED_EXTENSIONS', ALLOWED_EXTENSIONS)):
|
||||||
new_filename = generate_new_file_name(filename)
|
filename = generate_new_file_name(filename)
|
||||||
new_filename = secure_filename(new_filename)
|
filename = secure_filename(filename)
|
||||||
file_content = file.read()
|
file.save(os.path.join(
|
||||||
compressed_data = lz4.frame.compress(file_content)
|
current_app.config['UPLOAD_DIRECTORY_FULL'], filename))
|
||||||
try:
|
|
||||||
CommonFileCRUD.add_file(
|
|
||||||
origin_name=filename,
|
|
||||||
file_name=new_filename,
|
|
||||||
binary=compressed_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.jsonify(file_name=new_filename)
|
return self.jsonify(file_name=filename)
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(e)
|
|
||||||
abort(400, ErrFormat.upload_failed.format(e))
|
|
||||||
|
|
||||||
abort(400, ErrFormat.file_type_not_allowed.format(filename))
|
abort(400, 'Extension not allow')
|
||||||
|
@@ -47,7 +47,7 @@ class CheckEmailServer(APIView):
|
|||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
receive_address = request.args.get('receive_address')
|
receive_address = request.args.get('receive_address')
|
||||||
info = request.values.get('info', {})
|
info = request.values.get('info')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ Flask==2.3.2
|
|||||||
Flask-Bcrypt==1.0.1
|
Flask-Bcrypt==1.0.1
|
||||||
Flask-Caching==2.0.2
|
Flask-Caching==2.0.2
|
||||||
Flask-Cors==4.0.0
|
Flask-Cors==4.0.0
|
||||||
Flask-Login>=0.6.2
|
Flask-Login==0.6.2
|
||||||
Flask-Migrate==2.5.2
|
Flask-Migrate==2.5.2
|
||||||
Flask-RESTful==0.3.10
|
Flask-RESTful==0.3.10
|
||||||
Flask-SQLAlchemy==2.5.0
|
Flask-SQLAlchemy==2.5.0
|
||||||
@@ -29,12 +29,12 @@ MarkupSafe==2.1.3
|
|||||||
marshmallow==2.20.2
|
marshmallow==2.20.2
|
||||||
more-itertools==5.0.0
|
more-itertools==5.0.0
|
||||||
msgpack-python==0.5.6
|
msgpack-python==0.5.6
|
||||||
Pillow>=10.0.1
|
Pillow==9.3.0
|
||||||
cryptography>=41.0.2
|
cryptography==41.0.2
|
||||||
PyJWT==2.4.0
|
PyJWT==2.4.0
|
||||||
PyMySQL==1.1.0
|
PyMySQL==1.1.0
|
||||||
ldap3==2.9.1
|
ldap3==2.9.1
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0
|
||||||
redis==4.6.0
|
redis==4.6.0
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
requests_oauthlib==1.3.1
|
requests_oauthlib==1.3.1
|
||||||
@@ -45,9 +45,9 @@ supervisor==4.0.3
|
|||||||
timeout-decorator==0.5.0
|
timeout-decorator==0.5.0
|
||||||
toposort==1.10
|
toposort==1.10
|
||||||
treelib==1.6.1
|
treelib==1.6.1
|
||||||
Werkzeug>=2.3.6
|
Werkzeug==2.3.6
|
||||||
WTForms==3.0.0
|
WTForms==3.0.0
|
||||||
shamir~=17.12.0
|
shamir~=17.12.0
|
||||||
|
hvac~=2.0.0
|
||||||
pycryptodomex>=3.19.0
|
pycryptodomex>=3.19.0
|
||||||
colorama>=0.4.6
|
colorama>=0.4.6
|
||||||
lz4>=4.3.2
|
|
@@ -11,10 +11,10 @@ from environs import Env
|
|||||||
env = Env()
|
env = Env()
|
||||||
env.read_env()
|
env.read_env()
|
||||||
|
|
||||||
ENV = env.str('FLASK_ENV', default='production')
|
ENV = env.str("FLASK_ENV", default="production")
|
||||||
DEBUG = ENV == 'development'
|
DEBUG = ENV == "development"
|
||||||
SECRET_KEY = env.str('SECRET_KEY')
|
SECRET_KEY = env.str("SECRET_KEY")
|
||||||
BCRYPT_LOG_ROUNDS = env.int('BCRYPT_LOG_ROUNDS', default=13)
|
BCRYPT_LOG_ROUNDS = env.int("BCRYPT_LOG_ROUNDS", default=13)
|
||||||
DEBUG_TB_ENABLED = DEBUG
|
DEBUG_TB_ENABLED = DEBUG
|
||||||
DEBUG_TB_INTERCEPT_REDIRECTS = False
|
DEBUG_TB_INTERCEPT_REDIRECTS = False
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ ERROR_CODES = [400, 401, 403, 404, 405, 500, 502]
|
|||||||
# # database
|
# # database
|
||||||
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
|
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
|
||||||
SQLALCHEMY_BINDS = {
|
SQLALCHEMY_BINDS = {
|
||||||
'user': 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
|
"user": 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
|
||||||
}
|
}
|
||||||
SQLALCHEMY_ECHO = False
|
SQLALCHEMY_ECHO = False
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
@@ -32,11 +32,11 @@ SQLALCHEMY_ENGINE_OPTIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# # cache
|
# # cache
|
||||||
CACHE_TYPE = 'redis'
|
CACHE_TYPE = "redis"
|
||||||
CACHE_REDIS_HOST = '127.0.0.1'
|
CACHE_REDIS_HOST = "127.0.0.1"
|
||||||
CACHE_REDIS_PORT = 6379
|
CACHE_REDIS_PORT = 6379
|
||||||
CACHE_REDIS_PASSWORD = ''
|
CACHE_REDIS_PASSWORD = ""
|
||||||
CACHE_KEY_PREFIX = 'CMDB::'
|
CACHE_KEY_PREFIX = "CMDB::"
|
||||||
CACHE_DEFAULT_TIMEOUT = 3000
|
CACHE_DEFAULT_TIMEOUT = 3000
|
||||||
|
|
||||||
# # log
|
# # log
|
||||||
@@ -55,10 +55,10 @@ DEFAULT_MAIL_SENDER = ''
|
|||||||
|
|
||||||
# # queue
|
# # queue
|
||||||
CELERY = {
|
CELERY = {
|
||||||
'broker_url': 'redis://127.0.0.1:6379/2',
|
"broker_url": 'redis://127.0.0.1:6379/2',
|
||||||
'result_backend': 'redis://127.0.0.1:6379/2',
|
"result_backend": "redis://127.0.0.1:6379/2",
|
||||||
'broker_vhost': '/',
|
"broker_vhost": "/",
|
||||||
'broker_connection_retry_on_startup': True
|
"broker_connection_retry_on_startup": True
|
||||||
}
|
}
|
||||||
ONCE = {
|
ONCE = {
|
||||||
'backend': 'celery_once.backends.Redis',
|
'backend': 'celery_once.backends.Redis',
|
||||||
@@ -67,81 +67,33 @@ ONCE = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# =============================== Authentication ===========================================================
|
# # SSO
|
||||||
|
CAS_SERVER = "http://sso.xxx.com"
|
||||||
|
CAS_VALIDATE_SERVER = "http://sso.xxx.com"
|
||||||
|
CAS_LOGIN_ROUTE = "/cas/login"
|
||||||
|
CAS_LOGOUT_ROUTE = "/cas/logout"
|
||||||
|
CAS_VALIDATE_ROUTE = "/cas/serviceValidate"
|
||||||
|
CAS_AFTER_LOGIN = "/"
|
||||||
|
DEFAULT_SERVICE = "http://127.0.0.1:8000"
|
||||||
|
|
||||||
# # CAS
|
# # ldap
|
||||||
CAS = dict(
|
AUTH_WITH_LDAP = False
|
||||||
enabled=False,
|
LDAP_SERVER = ''
|
||||||
cas_server='https://{your-CASServer-hostname}',
|
LDAP_DOMAIN = ''
|
||||||
cas_validate_server='https://{your-CASServer-hostname}',
|
LDAP_USER_DN = 'cn={},ou=users,dc=xxx,dc=com'
|
||||||
cas_login_route='/cas/built-in/cas/login',
|
|
||||||
cas_logout_route='/cas/built-in/cas/logout',
|
|
||||||
cas_validate_route='/cas/built-in/cas/serviceValidate',
|
|
||||||
cas_after_login='/',
|
|
||||||
cas_user_map={
|
|
||||||
'username': {'tag': 'cas:user'},
|
|
||||||
'nickname': {'tag': 'cas:attribute', 'attrs': {'name': 'displayName'}},
|
|
||||||
'email': {'tag': 'cas:attribute', 'attrs': {'name': 'email'}},
|
|
||||||
'mobile': {'tag': 'cas:attribute', 'attrs': {'name': 'phone'}},
|
|
||||||
'avatar': {'tag': 'cas:attribute', 'attrs': {'name': 'avatar'}},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# # OAuth2.0
|
|
||||||
OAUTH2 = dict(
|
|
||||||
enabled=False,
|
|
||||||
client_id='',
|
|
||||||
client_secret='',
|
|
||||||
authorize_url='https://{your-OAuth2Server-hostname}/login/oauth/authorize',
|
|
||||||
token_url='https://{your-OAuth2Server-hostname}/api/login/oauth/access_token',
|
|
||||||
scopes=['profile', 'email'],
|
|
||||||
user_info={
|
|
||||||
'url': 'https://{your-OAuth2Server-hostname}/api/userinfo',
|
|
||||||
'email': 'email',
|
|
||||||
'username': 'name',
|
|
||||||
'avatar': 'picture'
|
|
||||||
},
|
|
||||||
after_login='/'
|
|
||||||
)
|
|
||||||
|
|
||||||
# # OIDC
|
|
||||||
OIDC = dict(
|
|
||||||
enabled=False,
|
|
||||||
client_id='',
|
|
||||||
client_secret='',
|
|
||||||
authorize_url='https://{your-OIDCServer-hostname}/login/oauth/authorize',
|
|
||||||
token_url='https://{your-OIDCServer-hostname}/api/login/oauth/access_token',
|
|
||||||
scopes=['openid', 'profile', 'email'],
|
|
||||||
user_info={
|
|
||||||
'url': 'https://{your-OIDCServer-hostname}/api/userinfo',
|
|
||||||
'email': 'email',
|
|
||||||
'username': 'name',
|
|
||||||
'avatar': 'picture'
|
|
||||||
},
|
|
||||||
after_login='/'
|
|
||||||
)
|
|
||||||
|
|
||||||
# # LDAP
|
|
||||||
LDAP = dict(
|
|
||||||
enabled=False,
|
|
||||||
ldap_server='',
|
|
||||||
ldap_domain='',
|
|
||||||
ldap_user_dn='cn={},ou=users,dc=xxx,dc=com'
|
|
||||||
)
|
|
||||||
# ==========================================================================================================
|
|
||||||
|
|
||||||
# # pagination
|
# # pagination
|
||||||
DEFAULT_PAGE_COUNT = 50
|
DEFAULT_PAGE_COUNT = 50
|
||||||
|
|
||||||
# # permission
|
# # permission
|
||||||
WHITE_LIST = ['127.0.0.1']
|
WHITE_LIST = ["127.0.0.1"]
|
||||||
USE_ACL = True
|
USE_ACL = True
|
||||||
|
|
||||||
# # elastic search
|
# # elastic search
|
||||||
ES_HOST = '127.0.0.1'
|
ES_HOST = '127.0.0.1'
|
||||||
USE_ES = False
|
USE_ES = False
|
||||||
|
|
||||||
BOOL_TRUE = ['true', 'TRUE', 'True', True, '1', 1, 'Yes', 'YES', 'yes', 'Y', 'y']
|
BOOL_TRUE = ['true', 'TRUE', 'True', True, '1', 1, "Yes", "YES", "yes", 'Y', 'y']
|
||||||
|
|
||||||
# # messenger
|
# # messenger
|
||||||
USE_MESSENGER = True
|
USE_MESSENGER = True
|
||||||
|
@@ -63,15 +63,14 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ant-design/colors": "^3.2.2",
|
"@ant-design/colors": "^3.2.2",
|
||||||
"@babel/core": "^7.23.2",
|
|
||||||
"@babel/polyfill": "^7.2.5",
|
"@babel/polyfill": "^7.2.5",
|
||||||
"@babel/preset-env": "^7.23.2",
|
|
||||||
"@vue/cli-plugin-babel": "4.5.17",
|
"@vue/cli-plugin-babel": "4.5.17",
|
||||||
"@vue/cli-plugin-eslint": "^4.0.5",
|
"@vue/cli-plugin-eslint": "^4.0.5",
|
||||||
"@vue/cli-plugin-unit-jest": "^4.0.5",
|
"@vue/cli-plugin-unit-jest": "^4.0.5",
|
||||||
"@vue/cli-service": "^4.0.5",
|
"@vue/cli-service": "^4.0.5",
|
||||||
"@vue/eslint-config-standard": "^4.0.0",
|
"@vue/eslint-config-standard": "^4.0.0",
|
||||||
"@vue/test-utils": "^1.0.0-beta.30",
|
"@vue/test-utils": "^1.0.0-beta.30",
|
||||||
|
"babel-core": "7.0.0-bridge.0",
|
||||||
"babel-jest": "^23.6.0",
|
"babel-jest": "^23.6.0",
|
||||||
"babel-plugin-import": "^1.11.0",
|
"babel-plugin-import": "^1.11.0",
|
||||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 3857903 */
|
font-family: "iconfont"; /* Project id 3857903 */
|
||||||
src: url('iconfont.woff2?t=1702544951995') format('woff2'),
|
src: url('iconfont.woff2?t=1698273699449') format('woff2'),
|
||||||
url('iconfont.woff?t=1702544951995') format('woff'),
|
url('iconfont.woff?t=1698273699449') format('woff'),
|
||||||
url('iconfont.ttf?t=1702544951995') format('truetype');
|
url('iconfont.ttf?t=1698273699449') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@@ -13,274 +13,6 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
.OAUTH2:before {
|
|
||||||
content: "\e8d8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.OIDC:before {
|
|
||||||
content: "\e8d6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.CAS:before {
|
|
||||||
content: "\e8d7";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-setting-auth:before {
|
|
||||||
content: "\e8d5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-setting-auth-selected:before {
|
|
||||||
content: "\e8d4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.a-itsm-knowledge2:before {
|
|
||||||
content: "\e8d2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-qrdownload:before {
|
|
||||||
content: "\e8d3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-playback:before {
|
|
||||||
content: "\e8d1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-disconnect:before {
|
|
||||||
content: "\e8d0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-publickey-selected:before {
|
|
||||||
content: "\e8cf";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-publickey:before {
|
|
||||||
content: "\e8ce";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-gateway:before {
|
|
||||||
content: "\e8b9";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-gateway-selected:before {
|
|
||||||
content: "\e8bf";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-account:before {
|
|
||||||
content: "\e8c0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-account-selected:before {
|
|
||||||
content: "\e8c1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-command:before {
|
|
||||||
content: "\e8c2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-command-selected:before {
|
|
||||||
content: "\e8c3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-assetlist:before {
|
|
||||||
content: "\e8c4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-assetlist-selected:before {
|
|
||||||
content: "\e8c5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-sessiononline:before {
|
|
||||||
content: "\e8c6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-sessiononline-selected:before {
|
|
||||||
content: "\e8c7";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-sessionhistory-selected:before {
|
|
||||||
content: "\e8c8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-sessionhistory:before {
|
|
||||||
content: "\e8c9";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-login:before {
|
|
||||||
content: "\e8ca";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-login-selected:before {
|
|
||||||
content: "\e8cb";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-operation:before {
|
|
||||||
content: "\e8cc";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-operation-selected:before {
|
|
||||||
content: "\e8cd";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-workstation-selected:before {
|
|
||||||
content: "\e8b7";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-workstation:before {
|
|
||||||
content: "\e8b8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-file-selected:before {
|
|
||||||
content: "\e8be";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-file:before {
|
|
||||||
content: "\e8bc";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-time:before {
|
|
||||||
content: "\e8bd";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-download:before {
|
|
||||||
content: "\e8bb";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-commandrecord:before {
|
|
||||||
content: "\e8ba";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-asset:before {
|
|
||||||
content: "\e8b6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-total_asset:before {
|
|
||||||
content: "\e8b5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-switch:before {
|
|
||||||
content: "\e8b4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-session:before {
|
|
||||||
content: "\e8b3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-connect:before {
|
|
||||||
content: "\e8b2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-login:before {
|
|
||||||
content: "\e8b1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-dashboard:before {
|
|
||||||
content: "\e8af";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-dashboard-selected:before {
|
|
||||||
content: "\e8b0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-recentsession:before {
|
|
||||||
content: "\e8ae";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-myassets:before {
|
|
||||||
content: "\e8ad";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-log:before {
|
|
||||||
content: "\e8aa";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-session-selected:before {
|
|
||||||
content: "\e8ab";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-session:before {
|
|
||||||
content: "\e8ac";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-log-selected:before {
|
|
||||||
content: "\e8a9";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-assets:before {
|
|
||||||
content: "\e8a7";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-assets-selected:before {
|
|
||||||
content: "\e8a8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-down:before {
|
|
||||||
content: "\e8a5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-up:before {
|
|
||||||
content: "\e8a6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-download:before {
|
|
||||||
content: "\e8a4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-print:before {
|
|
||||||
content: "\e8a3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-view:before {
|
|
||||||
content: "\e8a2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-word:before {
|
|
||||||
content: "\e8a1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-custom:before {
|
|
||||||
content: "\e89e";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-prometheus:before {
|
|
||||||
content: "\e89f";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-zabbix:before {
|
|
||||||
content: "\e8a0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-mainpeople:before {
|
|
||||||
content: "\e89a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-deputypeople:before {
|
|
||||||
content: "\e89d";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-setting-duty:before {
|
|
||||||
content: "\e89c";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-setting-duty-selected:before {
|
|
||||||
content: "\e89b";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-sequential:before {
|
|
||||||
content: "\e899";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-close:before {
|
|
||||||
content: "\e898";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-handle:before {
|
|
||||||
content: "\e897";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-table:before {
|
|
||||||
content: "\e896";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xianxing-password:before {
|
.icon-xianxing-password:before {
|
||||||
content: "\e894";
|
content: "\e894";
|
||||||
}
|
}
|
||||||
@@ -289,11 +21,11 @@
|
|||||||
content: "\e895";
|
content: "\e895";
|
||||||
}
|
}
|
||||||
|
|
||||||
.itsm-download-all:before {
|
.a-itsm-oneclickdownload:before {
|
||||||
content: "\e892";
|
content: "\e892";
|
||||||
}
|
}
|
||||||
|
|
||||||
.itsm-download-package:before {
|
.a-itsm-packagedownload:before {
|
||||||
content: "\e893";
|
content: "\e893";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -5,475 +5,6 @@
|
|||||||
"css_prefix_text": "",
|
"css_prefix_text": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
{
|
|
||||||
"icon_id": "38566548",
|
|
||||||
"name": "OAuth2.0",
|
|
||||||
"font_class": "OAUTH2",
|
|
||||||
"unicode": "e8d8",
|
|
||||||
"unicode_decimal": 59608
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38566584",
|
|
||||||
"name": "OIDC",
|
|
||||||
"font_class": "OIDC",
|
|
||||||
"unicode": "e8d6",
|
|
||||||
"unicode_decimal": 59606
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38566578",
|
|
||||||
"name": "cas",
|
|
||||||
"font_class": "CAS",
|
|
||||||
"unicode": "e8d7",
|
|
||||||
"unicode_decimal": 59607
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38547395",
|
|
||||||
"name": "setting-authentication",
|
|
||||||
"font_class": "ops-setting-auth",
|
|
||||||
"unicode": "e8d5",
|
|
||||||
"unicode_decimal": 59605
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38547389",
|
|
||||||
"name": "setting-authentication-selected",
|
|
||||||
"font_class": "ops-setting-auth-selected",
|
|
||||||
"unicode": "e8d4",
|
|
||||||
"unicode_decimal": 59604
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38533133",
|
|
||||||
"name": "itsm-knowledge (2)",
|
|
||||||
"font_class": "a-itsm-knowledge2",
|
|
||||||
"unicode": "e8d2",
|
|
||||||
"unicode_decimal": 59602
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38531868",
|
|
||||||
"name": "itsm-QRcode",
|
|
||||||
"font_class": "itsm-qrdownload",
|
|
||||||
"unicode": "e8d3",
|
|
||||||
"unicode_decimal": 59603
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38413515",
|
|
||||||
"name": "oneterm-playback",
|
|
||||||
"font_class": "oneterm-playback",
|
|
||||||
"unicode": "e8d1",
|
|
||||||
"unicode_decimal": 59601
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38413481",
|
|
||||||
"name": "oneterm-disconnect",
|
|
||||||
"font_class": "oneterm-disconnect",
|
|
||||||
"unicode": "e8d0",
|
|
||||||
"unicode_decimal": 59600
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38407867",
|
|
||||||
"name": "oneterm-key-selected",
|
|
||||||
"font_class": "ops-oneterm-publickey-selected",
|
|
||||||
"unicode": "e8cf",
|
|
||||||
"unicode_decimal": 59599
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38407915",
|
|
||||||
"name": "oneterm-key",
|
|
||||||
"font_class": "ops-oneterm-publickey",
|
|
||||||
"unicode": "e8ce",
|
|
||||||
"unicode_decimal": 59598
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311855",
|
|
||||||
"name": "oneterm-gateway",
|
|
||||||
"font_class": "ops-oneterm-gateway",
|
|
||||||
"unicode": "e8b9",
|
|
||||||
"unicode_decimal": 59577
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311938",
|
|
||||||
"name": "oneterm-gateway-selected",
|
|
||||||
"font_class": "ops-oneterm-gateway-selected",
|
|
||||||
"unicode": "e8bf",
|
|
||||||
"unicode_decimal": 59583
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311957",
|
|
||||||
"name": "oneterm-account",
|
|
||||||
"font_class": "ops-oneterm-account",
|
|
||||||
"unicode": "e8c0",
|
|
||||||
"unicode_decimal": 59584
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311961",
|
|
||||||
"name": "oneterm-account-selected",
|
|
||||||
"font_class": "ops-oneterm-account-selected",
|
|
||||||
"unicode": "e8c1",
|
|
||||||
"unicode_decimal": 59585
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311974",
|
|
||||||
"name": "oneterm-command",
|
|
||||||
"font_class": "ops-oneterm-command",
|
|
||||||
"unicode": "e8c2",
|
|
||||||
"unicode_decimal": 59586
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311976",
|
|
||||||
"name": "oneterm-command-selected",
|
|
||||||
"font_class": "ops-oneterm-command-selected",
|
|
||||||
"unicode": "e8c3",
|
|
||||||
"unicode_decimal": 59587
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311979",
|
|
||||||
"name": "oneterm-asset_list",
|
|
||||||
"font_class": "ops-oneterm-assetlist",
|
|
||||||
"unicode": "e8c4",
|
|
||||||
"unicode_decimal": 59588
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311985",
|
|
||||||
"name": "oneterm-asset_list-selected",
|
|
||||||
"font_class": "ops-oneterm-assetlist-selected",
|
|
||||||
"unicode": "e8c5",
|
|
||||||
"unicode_decimal": 59589
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312030",
|
|
||||||
"name": "oneterm-online",
|
|
||||||
"font_class": "ops-oneterm-sessiononline",
|
|
||||||
"unicode": "e8c6",
|
|
||||||
"unicode_decimal": 59590
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312152",
|
|
||||||
"name": "oneterm-online-selected",
|
|
||||||
"font_class": "ops-oneterm-sessiononline-selected",
|
|
||||||
"unicode": "e8c7",
|
|
||||||
"unicode_decimal": 59591
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312154",
|
|
||||||
"name": "oneterm-history-selected",
|
|
||||||
"font_class": "ops-oneterm-sessionhistory-selected",
|
|
||||||
"unicode": "e8c8",
|
|
||||||
"unicode_decimal": 59592
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312155",
|
|
||||||
"name": "oneterm-history",
|
|
||||||
"font_class": "ops-oneterm-sessionhistory",
|
|
||||||
"unicode": "e8c9",
|
|
||||||
"unicode_decimal": 59593
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312404",
|
|
||||||
"name": "oneterm-entry_log",
|
|
||||||
"font_class": "ops-oneterm-login",
|
|
||||||
"unicode": "e8ca",
|
|
||||||
"unicode_decimal": 59594
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312423",
|
|
||||||
"name": "oneterm-entry_log-selected",
|
|
||||||
"font_class": "ops-oneterm-login-selected",
|
|
||||||
"unicode": "e8cb",
|
|
||||||
"unicode_decimal": 59595
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312426",
|
|
||||||
"name": "oneterm-operation_log",
|
|
||||||
"font_class": "ops-oneterm-operation",
|
|
||||||
"unicode": "e8cc",
|
|
||||||
"unicode_decimal": 59596
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312445",
|
|
||||||
"name": "oneterm-operation_log-selected",
|
|
||||||
"font_class": "ops-oneterm-operation-selected",
|
|
||||||
"unicode": "e8cd",
|
|
||||||
"unicode_decimal": 59597
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38307876",
|
|
||||||
"name": "oneterm-workstation-selected",
|
|
||||||
"font_class": "ops-oneterm-workstation-selected",
|
|
||||||
"unicode": "e8b7",
|
|
||||||
"unicode_decimal": 59575
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38307871",
|
|
||||||
"name": "oneterm-workstation",
|
|
||||||
"font_class": "ops-oneterm-workstation",
|
|
||||||
"unicode": "e8b8",
|
|
||||||
"unicode_decimal": 59576
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38302246",
|
|
||||||
"name": "oneterm-file-selected",
|
|
||||||
"font_class": "oneterm-file-selected",
|
|
||||||
"unicode": "e8be",
|
|
||||||
"unicode_decimal": 59582
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38302255",
|
|
||||||
"name": "oneterm-file",
|
|
||||||
"font_class": "oneterm-file",
|
|
||||||
"unicode": "e8bc",
|
|
||||||
"unicode_decimal": 59580
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38203528",
|
|
||||||
"name": "oneterm-time",
|
|
||||||
"font_class": "oneterm-time",
|
|
||||||
"unicode": "e8bd",
|
|
||||||
"unicode_decimal": 59581
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38203331",
|
|
||||||
"name": "oneterm-download",
|
|
||||||
"font_class": "oneterm-download",
|
|
||||||
"unicode": "e8bb",
|
|
||||||
"unicode_decimal": 59579
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38201351",
|
|
||||||
"name": "oneterm-command record",
|
|
||||||
"font_class": "oneterm-commandrecord",
|
|
||||||
"unicode": "e8ba",
|
|
||||||
"unicode_decimal": 59578
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199341",
|
|
||||||
"name": "oneterm-connected assets",
|
|
||||||
"font_class": "oneterm-asset",
|
|
||||||
"unicode": "e8b6",
|
|
||||||
"unicode_decimal": 59574
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199350",
|
|
||||||
"name": "oneterm-total assets",
|
|
||||||
"font_class": "oneterm-total_asset",
|
|
||||||
"unicode": "e8b5",
|
|
||||||
"unicode_decimal": 59573
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199303",
|
|
||||||
"name": "oneterm-switch (3)",
|
|
||||||
"font_class": "oneterm-switch",
|
|
||||||
"unicode": "e8b4",
|
|
||||||
"unicode_decimal": 59572
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199317",
|
|
||||||
"name": "oneterm-session",
|
|
||||||
"font_class": "oneterm-session",
|
|
||||||
"unicode": "e8b3",
|
|
||||||
"unicode_decimal": 59571
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199339",
|
|
||||||
"name": "oneterm-connection",
|
|
||||||
"font_class": "oneterm-connect",
|
|
||||||
"unicode": "e8b2",
|
|
||||||
"unicode_decimal": 59570
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38198321",
|
|
||||||
"name": "oneterm-log in",
|
|
||||||
"font_class": "oneterm-login",
|
|
||||||
"unicode": "e8b1",
|
|
||||||
"unicode_decimal": 59569
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194554",
|
|
||||||
"name": "oneterm-dashboard",
|
|
||||||
"font_class": "ops-oneterm-dashboard",
|
|
||||||
"unicode": "e8af",
|
|
||||||
"unicode_decimal": 59567
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194525",
|
|
||||||
"name": "oneterm-dashboard-selected",
|
|
||||||
"font_class": "ops-oneterm-dashboard-selected",
|
|
||||||
"unicode": "e8b0",
|
|
||||||
"unicode_decimal": 59568
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194352",
|
|
||||||
"name": "oneterm-recent session",
|
|
||||||
"font_class": "oneterm-recentsession",
|
|
||||||
"unicode": "e8ae",
|
|
||||||
"unicode_decimal": 59566
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194383",
|
|
||||||
"name": "oneterm-my assets",
|
|
||||||
"font_class": "oneterm-myassets",
|
|
||||||
"unicode": "e8ad",
|
|
||||||
"unicode_decimal": 59565
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194089",
|
|
||||||
"name": "oneterm-log",
|
|
||||||
"font_class": "ops-oneterm-log",
|
|
||||||
"unicode": "e8aa",
|
|
||||||
"unicode_decimal": 59562
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194088",
|
|
||||||
"name": "oneterm-conversation-selected",
|
|
||||||
"font_class": "ops-oneterm-session-selected",
|
|
||||||
"unicode": "e8ab",
|
|
||||||
"unicode_decimal": 59563
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194065",
|
|
||||||
"name": "oneterm-conversation",
|
|
||||||
"font_class": "ops-oneterm-session",
|
|
||||||
"unicode": "e8ac",
|
|
||||||
"unicode_decimal": 59564
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194105",
|
|
||||||
"name": "oneterm-log-selected",
|
|
||||||
"font_class": "ops-oneterm-log-selected",
|
|
||||||
"unicode": "e8a9",
|
|
||||||
"unicode_decimal": 59561
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194054",
|
|
||||||
"name": "oneterm-assets",
|
|
||||||
"font_class": "ops-oneterm-assets",
|
|
||||||
"unicode": "e8a7",
|
|
||||||
"unicode_decimal": 59559
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194055",
|
|
||||||
"name": "oneterm-assets-selected",
|
|
||||||
"font_class": "ops-oneterm-assets-selected",
|
|
||||||
"unicode": "e8a8",
|
|
||||||
"unicode_decimal": 59560
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38123087",
|
|
||||||
"name": "itsm-down",
|
|
||||||
"font_class": "itsm-down",
|
|
||||||
"unicode": "e8a5",
|
|
||||||
"unicode_decimal": 59557
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38123084",
|
|
||||||
"name": "itsm-up",
|
|
||||||
"font_class": "itsm-up",
|
|
||||||
"unicode": "e8a6",
|
|
||||||
"unicode_decimal": 59558
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38105374",
|
|
||||||
"name": "itsm-download",
|
|
||||||
"font_class": "itsm-download",
|
|
||||||
"unicode": "e8a4",
|
|
||||||
"unicode_decimal": 59556
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38105235",
|
|
||||||
"name": "itsm-print",
|
|
||||||
"font_class": "itsm-print",
|
|
||||||
"unicode": "e8a3",
|
|
||||||
"unicode_decimal": 59555
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38104997",
|
|
||||||
"name": "itsm-view",
|
|
||||||
"font_class": "itsm-view",
|
|
||||||
"unicode": "e8a2",
|
|
||||||
"unicode_decimal": 59554
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38105129",
|
|
||||||
"name": "itsm-word",
|
|
||||||
"font_class": "itsm-word",
|
|
||||||
"unicode": "e8a1",
|
|
||||||
"unicode_decimal": 59553
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38095730",
|
|
||||||
"name": "datainsight-custom",
|
|
||||||
"font_class": "datainsight-custom",
|
|
||||||
"unicode": "e89e",
|
|
||||||
"unicode_decimal": 59550
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38095729",
|
|
||||||
"name": "datainsight-prometheus",
|
|
||||||
"font_class": "datainsight-prometheus",
|
|
||||||
"unicode": "e89f",
|
|
||||||
"unicode_decimal": 59551
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38095728",
|
|
||||||
"name": "datainsight-zabbix",
|
|
||||||
"font_class": "datainsight-zabbix",
|
|
||||||
"unicode": "e8a0",
|
|
||||||
"unicode_decimal": 59552
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37944507",
|
|
||||||
"name": "setting-main people",
|
|
||||||
"font_class": "setting-mainpeople",
|
|
||||||
"unicode": "e89a",
|
|
||||||
"unicode_decimal": 59546
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37944503",
|
|
||||||
"name": "setting-deputy people",
|
|
||||||
"font_class": "setting-deputypeople",
|
|
||||||
"unicode": "e89d",
|
|
||||||
"unicode_decimal": 59549
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37940080",
|
|
||||||
"name": "ops-setting-duty",
|
|
||||||
"font_class": "ops-setting-duty",
|
|
||||||
"unicode": "e89c",
|
|
||||||
"unicode_decimal": 59548
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37940033",
|
|
||||||
"name": "ops-setting-duty-selected",
|
|
||||||
"font_class": "ops-setting-duty-selected",
|
|
||||||
"unicode": "e89b",
|
|
||||||
"unicode_decimal": 59547
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37841524",
|
|
||||||
"name": "datainsight-sequential",
|
|
||||||
"font_class": "datainsight-sequential",
|
|
||||||
"unicode": "e899",
|
|
||||||
"unicode_decimal": 59545
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37841535",
|
|
||||||
"name": "datainsight-close",
|
|
||||||
"font_class": "datainsight-close",
|
|
||||||
"unicode": "e898",
|
|
||||||
"unicode_decimal": 59544
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37841537",
|
|
||||||
"name": "datainsight-handle",
|
|
||||||
"font_class": "datainsight-handle",
|
|
||||||
"unicode": "e897",
|
|
||||||
"unicode_decimal": 59543
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37841515",
|
|
||||||
"name": "datainsight-table",
|
|
||||||
"font_class": "datainsight-table",
|
|
||||||
"unicode": "e896",
|
|
||||||
"unicode_decimal": 59542
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"icon_id": "37830610",
|
"icon_id": "37830610",
|
||||||
"name": "icon-xianxing-password",
|
"name": "icon-xianxing-password",
|
||||||
@@ -491,14 +22,14 @@
|
|||||||
{
|
{
|
||||||
"icon_id": "37822199",
|
"icon_id": "37822199",
|
||||||
"name": "itsm-oneclick download",
|
"name": "itsm-oneclick download",
|
||||||
"font_class": "itsm-download-all",
|
"font_class": "a-itsm-oneclickdownload",
|
||||||
"unicode": "e892",
|
"unicode": "e892",
|
||||||
"unicode_decimal": 59538
|
"unicode_decimal": 59538
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "37822198",
|
"icon_id": "37822198",
|
||||||
"name": "itsm-package download",
|
"name": "itsm-package download",
|
||||||
"font_class": "itsm-download-package",
|
"font_class": "a-itsm-packagedownload",
|
||||||
"unicode": "e893",
|
"unicode": "e893",
|
||||||
"unicode_decimal": 59539
|
"unicode_decimal": 59539
|
||||||
},
|
},
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,39 +0,0 @@
|
|||||||
import { axios } from '@/utils/request'
|
|
||||||
|
|
||||||
export function getAuthData(data_type) {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/${data_type}`,
|
|
||||||
method: 'get',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postAuthData(data_type, data) {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/${data_type}`,
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function putAuthData(data_type, id, data) {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/${data_type}/${id}`,
|
|
||||||
method: 'put',
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAuthDataEnable() {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/enable_list`,
|
|
||||||
method: 'get',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function testLDAP(test_type, data) {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/LDAP/test?test_type=${test_type}`,
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
@@ -1,6 +1,8 @@
|
|||||||
|
import config from '@/config/setting'
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
Login: '/v1/acl/login',
|
Login: config.useSSO ? '/api/sso/login' : '/v1/acl/login',
|
||||||
Logout: '/v1/acl/logout',
|
Logout: config.useSSO ? '/api/sso/logout' : '/v1/acl/logout',
|
||||||
ForgePassword: '/auth/forge-password',
|
ForgePassword: '/auth/forge-password',
|
||||||
Register: '/auth/register',
|
Register: '/auth/register',
|
||||||
twoStepCode: '/auth/2step-code',
|
twoStepCode: '/auth/2step-code',
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import api from './index'
|
import api from './index'
|
||||||
import { axios } from '@/utils/request'
|
import { axios } from '@/utils/request'
|
||||||
|
import config from '@/config/setting'
|
||||||
/**
|
/**
|
||||||
* login func
|
* login func
|
||||||
* parameter: {
|
* parameter: {
|
||||||
@@ -11,10 +12,9 @@ import { axios } from '@/utils/request'
|
|||||||
* @param parameter
|
* @param parameter
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
export function login(data, auth_type) {
|
export function login(data) {
|
||||||
if (auth_type) {
|
if (config.useSSO) {
|
||||||
localStorage.setItem('ops_auth_type', auth_type)
|
window.location.href = config.ssoLoginUrl
|
||||||
window.location.href = `/api/${auth_type.toLowerCase()}/login`
|
|
||||||
} else {
|
} else {
|
||||||
return axios({
|
return axios({
|
||||||
url: api.Login,
|
url: api.Login,
|
||||||
@@ -43,15 +43,17 @@ export function getInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function logout() {
|
export function logout() {
|
||||||
const auth_type = localStorage.getItem('ops_auth_type')
|
if (config.useSSO) {
|
||||||
localStorage.clear()
|
window.location.replace(api.Logout)
|
||||||
return axios({
|
} else {
|
||||||
url: auth_type ? `/${auth_type.toLowerCase()}/logout` : api.Logout,
|
return axios({
|
||||||
method: auth_type ? 'get' : 'post',
|
url: api.Logout,
|
||||||
headers: {
|
method: 'post',
|
||||||
'Content-Type': 'application/json;charset=UTF-8'
|
headers: {
|
||||||
}
|
'Content-Type': 'application/json;charset=UTF-8'
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 78 KiB |
@@ -95,10 +95,6 @@ export default {
|
|||||||
const unsubTree = subscribeTreeView(citypeId, '')
|
const unsubTree = subscribeTreeView(citypeId, '')
|
||||||
Promise.all([unsubCIType, unsubTree]).then(() => {
|
Promise.all([unsubCIType, unsubTree]).then(() => {
|
||||||
that.$message.success('取消订阅成功')
|
that.$message.success('取消订阅成功')
|
||||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
|
||||||
if (Number(citypeId) === Number(lastTypeId)) {
|
|
||||||
localStorage.setItem('ops_ci_typeid', '')
|
|
||||||
}
|
|
||||||
// 删除路由
|
// 删除路由
|
||||||
const href = window.location.href
|
const href = window.location.href
|
||||||
const hrefSplit = href.split('/')
|
const hrefSplit = href.split('/')
|
||||||
|
@@ -66,8 +66,10 @@ export default {
|
|||||||
|
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '确认注销登录 ?',
|
content: '真的要注销登录吗 ?',
|
||||||
onOk() {
|
onOk() {
|
||||||
|
// localStorage.removeItem('ops_cityps_currentId')
|
||||||
|
localStorage.clear()
|
||||||
return that.Logout()
|
return that.Logout()
|
||||||
},
|
},
|
||||||
onCancel() {},
|
onCancel() {},
|
||||||
|
@@ -2,6 +2,7 @@ const appConfig = {
|
|||||||
buildModules: ['cmdb', 'acl'], // 需要编译的模块
|
buildModules: ['cmdb', 'acl'], // 需要编译的模块
|
||||||
redirectTo: '/cmdb', // 首页的重定向路径
|
redirectTo: '/cmdb', // 首页的重定向路径
|
||||||
buildAclToModules: true, // 是否在各个应用下 内联权限管理
|
buildAclToModules: true, // 是否在各个应用下 内联权限管理
|
||||||
|
ssoLogoutURL: '/api/sso/logout',
|
||||||
showDocs: false,
|
showDocs: false,
|
||||||
useEncryption: false,
|
useEncryption: false,
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* 项目默认配置项
|
* 项目默认配置项
|
||||||
|
* useSSO - 是否启用单点登录, 默认为否, 可以根据需要接入到公司的单点登录系统
|
||||||
* primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage
|
* primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage
|
||||||
* navTheme - sidebar theme ['dark', 'light'] 两种主题
|
* navTheme - sidebar theme ['dark', 'light'] 两种主题
|
||||||
* colorWeak - 色盲模式
|
* colorWeak - 色盲模式
|
||||||
@@ -14,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
useSSO: false,
|
||||||
|
ssoLoginUrl: '/api/sso/login',
|
||||||
primaryColor: '#1890ff', // primary color of ant design
|
primaryColor: '#1890ff', // primary color of ant design
|
||||||
navTheme: 'dark', // theme for nav menu
|
navTheme: 'dark', // theme for nav menu
|
||||||
layout: 'sidemenu', // nav menu position: sidemenu or topmenu
|
layout: 'sidemenu', // nav menu position: sidemenu or topmenu
|
||||||
|
@@ -6,6 +6,7 @@ import store from './store'
|
|||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
|
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
|
||||||
|
import config from '@/config/setting'
|
||||||
import { ACCESS_TOKEN } from './store/global/mutation-types'
|
import { ACCESS_TOKEN } from './store/global/mutation-types'
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false })
|
||||||
@@ -15,16 +16,16 @@ const whitePath = ['/user/login', '/user/logout', '/user/register', '/api/sso/lo
|
|||||||
|
|
||||||
// 此处不处理登录, 只处理 是否有用户信息的认证 前端permission的处理 axios处理401 -> 登录
|
// 此处不处理登录, 只处理 是否有用户信息的认证 前端permission的处理 axios处理401 -> 登录
|
||||||
// 登录页面处理处理 是否使用单点登录
|
// 登录页面处理处理 是否使用单点登录
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
NProgress.start() // start progress bar
|
NProgress.start() // start progress bar
|
||||||
to.meta && (!!to.meta.title && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
|
to.meta && (!!to.meta.title && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
|
||||||
|
|
||||||
const authed = store.state.authed
|
const authed = store.state.authed
|
||||||
const auth_type = localStorage.getItem('ops_auth_type')
|
|
||||||
|
|
||||||
if (whitePath.includes(to.path)) {
|
if (whitePath.includes(to.path)) {
|
||||||
next()
|
next()
|
||||||
} else if ((auth_type || (!auth_type && Vue.ls.get(ACCESS_TOKEN))) && store.getters.roles.length === 0) {
|
} else if ((config.useSSO || (!config.useSSO && Vue.ls.get(ACCESS_TOKEN))) && store.getters.roles.length === 0) {
|
||||||
store.dispatch('GetAuthDataEnable')
|
|
||||||
store.dispatch('GetInfo').then(res => {
|
store.dispatch('GetInfo').then(res => {
|
||||||
const roles = res.result && res.result.role
|
const roles = res.result && res.result.role
|
||||||
store.dispatch("loadAllUsers")
|
store.dispatch("loadAllUsers")
|
||||||
@@ -45,17 +46,10 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
setTimeout(() => { store.dispatch('Logout') }, 3000)
|
setTimeout(() => { store.dispatch('Logout') }, 3000)
|
||||||
})
|
})
|
||||||
} else if (to.path === '/user/login' && !auth_type && store.getters.roles.length !== 0) {
|
} else if (to.path === '/user/login' && !config.useSSO && store.getters.roles.length !== 0) {
|
||||||
next({ path: '/' })
|
next({ path: '/' })
|
||||||
} else if (!auth_type && !Vue.ls.get(ACCESS_TOKEN) && to.path !== '/user/login') {
|
} else if (!config.useSSO && !Vue.ls.get(ACCESS_TOKEN) && to.path !== '/user/login') {
|
||||||
await store.dispatch('GetAuthDataEnable')
|
next({ path: '/user/login', query: { redirect: to.fullPath } })
|
||||||
const { enable_list = [] } = store?.state?.user?.auth_enable ?? {}
|
|
||||||
const _enable_list = enable_list.filter(en => en.auth_type !== 'LDAP')
|
|
||||||
if (_enable_list.length === 1) {
|
|
||||||
next({ path: '/user/logout', query: { redirect: to.fullPath } })
|
|
||||||
} else {
|
|
||||||
next({ path: '/user/login', query: { redirect: to.fullPath } })
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
@@ -68,7 +68,6 @@
|
|||||||
ref="xTable"
|
ref="xTable"
|
||||||
row-id="id"
|
row-id="id"
|
||||||
show-overflow
|
show-overflow
|
||||||
resizable
|
|
||||||
>
|
>
|
||||||
<!-- 1 -->
|
<!-- 1 -->
|
||||||
<vxe-table-column type="checkbox" fixed="left" :width="45"></vxe-table-column>
|
<vxe-table-column type="checkbox" fixed="left" :width="45"></vxe-table-column>
|
||||||
|
@@ -133,8 +133,8 @@ export default {
|
|||||||
if (newVal) {
|
if (newVal) {
|
||||||
this.tableData = this.allUsers.filter(
|
this.tableData = this.allUsers.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
(item.username && item.username.toLowerCase().includes(newVal.toLowerCase())) ||
|
item.username.toLowerCase().includes(newVal.toLowerCase()) ||
|
||||||
(item.nickname && item.nickname.toLowerCase().includes(newVal.toLowerCase()))
|
item.nickname.toLowerCase().includes(newVal.toLowerCase())
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
this.tableData = this.allUsers
|
this.tableData = this.allUsers
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import { axios } from '@/utils/request'
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
export function getFirstCIsByCiId(ciId) {
|
export function getFirstCIs(ciId) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/v0.1/ci_relations/' + ciId + '/first_cis',
|
url: '/v0.1/ci_relations/' + ciId + '/first_cis',
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSecondCIsByCiId(ciId) {
|
export function getSecondCIs(ciId) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/v0.1/ci_relations/' + ciId + '/second_cis',
|
url: '/v0.1/ci_relations/' + ciId + '/second_cis',
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
@@ -30,11 +30,11 @@ export function statisticsCIRelation(params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 批量添加子节点
|
// 批量添加子节点
|
||||||
export function batchUpdateCIRelationChildren(ciIds, parents, ancestor_ids = undefined) {
|
export function batchUpdateCIRelationChildren(ciIds, parents) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/v0.1/ci_relations/batch',
|
url: '/v0.1/ci_relations/batch',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { ci_ids: ciIds, parents, ancestor_ids }
|
data: { ci_ids: ciIds, parents: parents }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,28 +48,26 @@ export function batchUpdateCIRelationParents(ciIds, children) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 批量删除
|
// 批量删除
|
||||||
export function batchDeleteCIRelation(ciIds, parents, ancestor_ids = undefined) {
|
export function batchDeleteCIRelation(ciIds, parents) {
|
||||||
return axios({
|
return axios({
|
||||||
url: '/v0.1/ci_relations/batch',
|
url: '/v0.1/ci_relations/batch',
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
data: { ci_ids: ciIds, parents, ancestor_ids }
|
data: { ci_ids: ciIds, parents: parents }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 单个添加
|
// 单个添加
|
||||||
export function addCIRelationView(firstCiId, secondCiId, data) {
|
export function addCIRelationView(firstCiId, secondCiId) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/v0.1/ci_relations/${firstCiId}/${secondCiId}`,
|
url: `/v0.1/ci_relations/${firstCiId}/${secondCiId}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 单个删除
|
// 单个删除
|
||||||
export function deleteCIRelationView(firstCiId, secondCiId, data) {
|
export function deleteCIRelationView(firstCiId, secondCiId) {
|
||||||
return axios({
|
return axios({
|
||||||
url: `/v0.1/ci_relations/${firstCiId}/${secondCiId}`,
|
url: `/v0.1/ci_relations/${firstCiId}/${secondCiId}`,
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
data
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -68,10 +68,3 @@ export function getRecursive_level2children(type_id) {
|
|||||||
method: 'GET'
|
method: 'GET'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCanEditByParentIdChildId(parent_id, child_id) {
|
|
||||||
return axios({
|
|
||||||
url: `/v0.1/ci_type_relations/${parent_id}/${child_id}/can_edit`,
|
|
||||||
method: 'GET'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@@ -16,14 +16,12 @@ export function processFile(fileObj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function uploadData(ciId, data) {
|
export function uploadData(ciId, data) {
|
||||||
|
data.ci_type = ciId
|
||||||
|
data.exist_policy = 'replace'
|
||||||
return axios({
|
return axios({
|
||||||
url: '/v0.1/ci',
|
url: '/v0.1/ci',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data,
|
||||||
...data,
|
|
||||||
ci_type: ciId,
|
|
||||||
exist_policy: 'replace'
|
|
||||||
},
|
|
||||||
isShowMessage: false
|
isShowMessage: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cmdb-batch-upload" :style="{ height: `${windowHeight - 64}px` }">
|
<div class="cmdb-batch-upload" :style="{ height: `${windowHeight - 64}px` }">
|
||||||
<div id="title">
|
<div id="title">
|
||||||
<ci-type-choice ref="ciTypeChoice" @getCiTypeAttr="showCiType" />
|
<ci-type-choice @getCiTypeAttr="showCiType" />
|
||||||
</div>
|
</div>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<upload-file-form
|
<upload-file-form :ciType="ciType" ref="uploadFileForm" @uploadDone="uploadDone"></upload-file-form>
|
||||||
:isUploading="isUploading"
|
|
||||||
:ciType="ciType"
|
|
||||||
ref="uploadFileForm"
|
|
||||||
@uploadDone="uploadDone"
|
|
||||||
></upload-file-form>
|
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24" v-if="ciType && uploadData.length">
|
<a-col :span="24" v-if="ciType && uploadData.length">
|
||||||
<CiUploadTable :ciTypeAttrs="ciTypeAttrs" ref="ciUploadTable" :uploadData="uploadData"></CiUploadTable>
|
<CiUploadTable :ciTypeAttrs="ciTypeAttrs" ref="ciUploadTable" :uploadData="uploadData"></CiUploadTable>
|
||||||
@@ -18,19 +13,15 @@
|
|||||||
<a-space size="large">
|
<a-space size="large">
|
||||||
<a-button type="primary" ghost @click="handleCancel">取消</a-button>
|
<a-button type="primary" ghost @click="handleCancel">取消</a-button>
|
||||||
<a-button @click="handleUpload" type="primary">上传</a-button>
|
<a-button @click="handleUpload" type="primary">上传</a-button>
|
||||||
<a-button v-if="hasError && !isUploading" @click="downloadError" type="primary">失败下载</a-button>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24" v-if="ciType">
|
<a-col :span="24">
|
||||||
<upload-result
|
<upload-result
|
||||||
ref="uploadResult"
|
ref="uploadResult"
|
||||||
:upLoadData="uploadData"
|
:upLoadData="uploadData"
|
||||||
:ciType="ciType"
|
:ciType="ciType"
|
||||||
:unique-field="uniqueField"
|
:unique-field="uniqueField"
|
||||||
:isUploading="isUploading"
|
|
||||||
@uploadResultDone="uploadResultDone"
|
|
||||||
@uploadResultError="uploadResultError"
|
|
||||||
></upload-result>
|
></upload-result>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -38,7 +29,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import moment from 'moment'
|
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import CiTypeChoice from './modules/CiTypeChoice'
|
import CiTypeChoice from './modules/CiTypeChoice'
|
||||||
import CiUploadTable from './modules/CiUploadTable'
|
import CiUploadTable from './modules/CiUploadTable'
|
||||||
@@ -61,8 +51,7 @@ export default {
|
|||||||
ciType: 0,
|
ciType: 0,
|
||||||
uniqueField: '',
|
uniqueField: '',
|
||||||
uniqueId: 0,
|
uniqueId: 0,
|
||||||
isUploading: false,
|
displayUpload: true,
|
||||||
hasError: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -70,12 +59,13 @@ export default {
|
|||||||
windowHeight: (state) => state.windowHeight,
|
windowHeight: (state) => state.windowHeight,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
inject: ['reload'],
|
||||||
methods: {
|
methods: {
|
||||||
showCiType(message) {
|
showCiType(message) {
|
||||||
this.ciTypeAttrs = message ?? {}
|
this.ciTypeAttrs = message
|
||||||
this.ciType = message?.type_id ?? 0
|
this.ciType = message.type_id
|
||||||
this.uniqueField = message?.unique ?? ''
|
this.uniqueField = message.unique
|
||||||
this.uniqueId = message?.unique_id ?? 0
|
this.uniqueId = message.unique_id
|
||||||
},
|
},
|
||||||
uploadDone(dataList) {
|
uploadDone(dataList) {
|
||||||
const _uploadData = filterNull(dataList).map((item, i) => {
|
const _uploadData = filterNull(dataList).map((item, i) => {
|
||||||
@@ -83,20 +73,7 @@ export default {
|
|||||||
const _ele = {}
|
const _ele = {}
|
||||||
item.forEach((ele, j) => {
|
item.forEach((ele, j) => {
|
||||||
if (ele !== undefined && ele !== null) {
|
if (ele !== undefined && ele !== null) {
|
||||||
const _find = this.ciTypeAttrs.attributes.find(
|
_ele[dataList[0][j]] = ele
|
||||||
(attr) => attr.alias === dataList[0][j] || attr.name === dataList[0][j]
|
|
||||||
)
|
|
||||||
if (_find?.value_type === '4' && typeof ele === 'number') {
|
|
||||||
_ele[dataList[0][j]] = moment(Math.round((ele - 25569) * 86400 * 1000 - 28800000)).format('YYYY-MM-DD')
|
|
||||||
} else if (_find?.value_type === '3' && typeof ele === 'number') {
|
|
||||||
_ele[dataList[0][j]] = moment(Math.round((ele - 25569) * 86400 * 1000 - 28800000)).format(
|
|
||||||
'YYYY-MM-DD HH:mm:ss'
|
|
||||||
)
|
|
||||||
} else if (_find?.value_type === '5' && typeof ele === 'number') {
|
|
||||||
_ele[dataList[0][j]] = moment(Math.round(ele * 86400 * 1000 - 28800000)).format('HH:mm:ss')
|
|
||||||
} else {
|
|
||||||
_ele[dataList[0][j]] = ele
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return _ele
|
return _ele
|
||||||
@@ -104,9 +81,6 @@ export default {
|
|||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
this.uploadData = _uploadData.slice(1)
|
this.uploadData = _uploadData.slice(1)
|
||||||
this.hasError = false
|
|
||||||
this.isUploading = false
|
|
||||||
this.$refs.uploadResult.visible = false
|
|
||||||
},
|
},
|
||||||
handleUpload() {
|
handleUpload() {
|
||||||
if (!this.ciType) {
|
if (!this.ciType) {
|
||||||
@@ -114,7 +88,6 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.uploadData && this.uploadData.length > 0) {
|
if (this.uploadData && this.uploadData.length > 0) {
|
||||||
this.isUploading = true
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.uploadResult.upload2Server()
|
this.$refs.uploadResult.upload2Server()
|
||||||
})
|
})
|
||||||
@@ -123,24 +96,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleCancel() {
|
handleCancel() {
|
||||||
if (!this.isUploading) {
|
this.reload()
|
||||||
this.showCiType(null)
|
|
||||||
this.$refs.ciTypeChoice.selectNum = null
|
|
||||||
this.hasError = false
|
|
||||||
} else {
|
|
||||||
this.$message.warning('批量上传已取消')
|
|
||||||
this.isUploading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
uploadResultDone() {
|
|
||||||
this.isUploading = false
|
|
||||||
},
|
|
||||||
uploadResultError(index) {
|
|
||||||
this.hasError = true
|
|
||||||
this.$refs.ciUploadTable.uploadResultError(index)
|
|
||||||
},
|
|
||||||
downloadError() {
|
|
||||||
this.$refs.ciUploadTable.downloadError()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
:style="{ width: '300px' }"
|
:style="{ width: '300px' }"
|
||||||
class="ops-select"
|
class="ops-select"
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
v-model="selectNum"
|
|
||||||
>
|
>
|
||||||
<a-select-option v-for="ciType in ciTypeList" :key="ciType.name" :value="ciType.id">{{
|
<a-select-option v-for="ciType in ciTypeList" :key="ciType.name" :value="ciType.id">{{
|
||||||
ciType.alias
|
ciType.alias
|
||||||
@@ -41,7 +40,7 @@
|
|||||||
全选
|
全选
|
||||||
</a-checkbox>
|
</a-checkbox>
|
||||||
<br />
|
<br />
|
||||||
<a-checkbox-group style="width:100%" v-model="checkedAttrs">
|
<a-checkbox-group v-model="checkedAttrs">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="6" v-for="item in selectCiTypeAttrList.attributes" :key="item.alias || item.name">
|
<a-col :span="6" v-for="item in selectCiTypeAttrList.attributes" :key="item.alias || item.name">
|
||||||
<a-checkbox :disabled="item.name === selectCiTypeAttrList.unique" :value="item.alias || item.name">
|
<a-checkbox :disabled="item.name === selectCiTypeAttrList.unique" :value="item.alias || item.name">
|
||||||
@@ -88,11 +87,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
|
||||||
import { downloadExcel } from '../../../utils/helper'
|
import { downloadExcel } from '../../../utils/helper'
|
||||||
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
||||||
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
||||||
import { getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
import { getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CiTypeChoice',
|
name: 'CiTypeChoice',
|
||||||
@@ -100,7 +98,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
ciTypeList: [],
|
ciTypeList: [],
|
||||||
ciTypeName: '',
|
ciTypeName: '',
|
||||||
selectNum: null,
|
selectNum: 0,
|
||||||
selectCiTypeAttrList: [],
|
selectCiTypeAttrList: [],
|
||||||
visible: false,
|
visible: false,
|
||||||
checkedAttrs: [],
|
checkedAttrs: [],
|
||||||
@@ -109,7 +107,6 @@ export default {
|
|||||||
parentsType: [],
|
parentsType: [],
|
||||||
parentsForm: {},
|
parentsForm: {},
|
||||||
checkedParents: [],
|
checkedParents: [],
|
||||||
canEdit: {},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: function() {
|
created: function() {
|
||||||
@@ -132,6 +129,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
selectCiType(el) {
|
selectCiType(el) {
|
||||||
// 当选择好模板类型时的回调函数
|
// 当选择好模板类型时的回调函数
|
||||||
|
this.selectNum = el
|
||||||
getCITypeAttributesById(el).then((res) => {
|
getCITypeAttributesById(el).then((res) => {
|
||||||
this.$emit('getCiTypeAttr', res)
|
this.$emit('getCiTypeAttr', res)
|
||||||
this.selectCiTypeAttrList = res
|
this.selectCiTypeAttrList = res
|
||||||
@@ -145,16 +143,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
openModal() {
|
openModal() {
|
||||||
getCITypeParent(this.selectNum).then(async (res) => {
|
getCITypeParent(this.selectNum).then((res) => {
|
||||||
for (let i = 0; i < res.parents.length; i++) {
|
this.parentsType = res.parents
|
||||||
await getCanEditByParentIdChildId(res.parents[i].id, this.selectNum).then((p_res) => {
|
|
||||||
this.canEdit = {
|
|
||||||
..._.cloneDeep(this.canEdit),
|
|
||||||
[res.parents[i].id]: p_res.result,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.parentsType = res.parents.filter((parent) => this.canEdit[parent.id])
|
|
||||||
const _parentsForm = {}
|
const _parentsForm = {}
|
||||||
res.parents.forEach((item) => {
|
res.parents.forEach((item) => {
|
||||||
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cmdb-batch-upload-table">
|
<div class="cmdb-batch-upload-table">
|
||||||
<vxe-table
|
<vxe-table
|
||||||
ref="xTable"
|
|
||||||
stripe
|
stripe
|
||||||
show-header-overflow
|
show-header-overflow
|
||||||
show-overflow=""
|
show-overflow=""
|
||||||
@@ -9,8 +8,6 @@
|
|||||||
class="ops-stripe-table"
|
class="ops-stripe-table"
|
||||||
:max-height="200"
|
:max-height="200"
|
||||||
:data="dataSource"
|
:data="dataSource"
|
||||||
resizable
|
|
||||||
:row-style="rowStyle"
|
|
||||||
>
|
>
|
||||||
<vxe-column type="seq" width="40" />
|
<vxe-column type="seq" width="40" />
|
||||||
<vxe-column
|
<vxe-column
|
||||||
@@ -39,9 +36,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {}
|
||||||
errorIndexList: [],
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
columns() {
|
columns() {
|
||||||
@@ -69,33 +64,7 @@ export default {
|
|||||||
return _.cloneDeep(this.uploadData)
|
return _.cloneDeep(this.uploadData)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
methods: {},
|
||||||
uploadData() {
|
|
||||||
this.errorIndexList = []
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
uploadResultError(index) {
|
|
||||||
const _errorIndexList = _.cloneDeep(this.errorIndexList)
|
|
||||||
_errorIndexList.push(index)
|
|
||||||
this.errorIndexList = _errorIndexList
|
|
||||||
},
|
|
||||||
rowStyle({ rowIndex }) {
|
|
||||||
if (this.errorIndexList.includes(rowIndex)) {
|
|
||||||
return 'color:red;'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
downloadError() {
|
|
||||||
const data = this.uploadData.filter((item, index) => this.errorIndexList.includes(index))
|
|
||||||
this.$refs.xTable.exportData({
|
|
||||||
data,
|
|
||||||
type: 'xlsx',
|
|
||||||
columnFilterMethod({ column }) {
|
|
||||||
return column.property
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
accept=".xls,.xlsx"
|
accept=".xls,.xlsx"
|
||||||
:showUploadList="false"
|
:showUploadList="false"
|
||||||
:fileList="fileList"
|
:fileList="fileList"
|
||||||
:disabled="!ciType || isUploading"
|
:disabled="!ciType"
|
||||||
>
|
>
|
||||||
<img :style="{ width: '80px', height: '80px' }" src="@/assets/file_upload.png" />
|
<img :style="{ width: '80px', height: '80px' }" src="@/assets/file_upload.png" />
|
||||||
<p class="ant-upload-text">点击或拖拽文件至此上传!</p>
|
<p class="ant-upload-text">点击或拖拽文件至此上传!</p>
|
||||||
@@ -29,11 +29,7 @@ export default {
|
|||||||
ciType: {
|
ciType: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
}
|
||||||
isUploading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -44,20 +40,7 @@ export default {
|
|||||||
percent: 0,
|
percent: 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
ciType: {
|
|
||||||
handler(newValue) {
|
|
||||||
if (!newValue) {
|
|
||||||
this.ciItemNum = 0
|
|
||||||
this.fileList = []
|
|
||||||
this.dataList = []
|
|
||||||
this.progressStatus = 'active'
|
|
||||||
this.percent = 0
|
|
||||||
this.$emit('uploadDone', this.dataList)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
customRequest(data) {
|
customRequest(data) {
|
||||||
this.fileList = [data.file]
|
this.fileList = [data.file]
|
||||||
|
@@ -34,10 +34,6 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
isUploading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
@@ -55,38 +51,33 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async sleep(n) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve()
|
||||||
|
}, n || 5)
|
||||||
|
})
|
||||||
|
},
|
||||||
async upload2Server() {
|
async upload2Server() {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
this.success = 0
|
this.success = 0
|
||||||
this.errorNum = 0
|
this.errorNum = 0
|
||||||
this.errorItems = []
|
this.errorItems = []
|
||||||
const floor = Math.ceil(this.total / 6)
|
for (let i = 0; i < this.total; i++) {
|
||||||
for (let i = 0; i < floor; i++) {
|
// await this.sleep(20)
|
||||||
if (this.isUploading) {
|
const item = this.upLoadData[i]
|
||||||
const itemList = this.upLoadData.slice(6 * i, 6 * i + 6)
|
await uploadData(this.ciType, item)
|
||||||
const promises = itemList.map((x) => uploadData(this.ciType, x))
|
.then((res) => {
|
||||||
await Promise.allSettled(promises)
|
console.log(res)
|
||||||
.then((res) => {
|
this.success += 1
|
||||||
res.forEach((r, j) => {
|
})
|
||||||
if (r.status === 'fulfilled') {
|
.catch((err) => {
|
||||||
this.success += 1
|
this.errorNum += 1
|
||||||
} else {
|
this.errorItems.push(((err.response || {}).data || {}).message || '请求出现错误,请稍后再试')
|
||||||
this.errorItems.push(r?.reason?.response?.data.message ?? '请求出现错误,请稍后再试')
|
})
|
||||||
this.errorNum += 1
|
.finally(() => {
|
||||||
this.$emit('uploadResultError', 6 * i + j)
|
this.complete += 1
|
||||||
}
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.complete += 6
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.isUploading) {
|
|
||||||
this.$emit('uploadResultDone')
|
|
||||||
this.$message.success('批量上传已完成')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -108,7 +108,7 @@
|
|||||||
<span>{{ col.title }}</span>
|
<span>{{ col.title }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="col.is_choice || col.is_password || col.is_list" #edit="{ row }">
|
<template v-if="col.is_choice || col.is_password" #edit="{ row }">
|
||||||
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
|
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
|
||||||
<a-select
|
<a-select
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
:getPopupContainer="(trigger) => trigger.parentElement"
|
||||||
@@ -145,18 +145,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select
|
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
|
||||||
:style="{ width: '100%', height: '32px' }"
|
|
||||||
v-model="row[col.field]"
|
|
||||||
placeholder="请选择"
|
|
||||||
v-else-if="col.is_list"
|
|
||||||
:showArrow="false"
|
|
||||||
mode="tags"
|
|
||||||
class="ci-table-edit-select"
|
|
||||||
allowClear
|
|
||||||
>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
|
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
|
||||||
@@ -666,19 +654,13 @@ export default {
|
|||||||
let errorNum = 0
|
let errorNum = 0
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.loadTip = `正在删除...`
|
this.loadTip = `正在删除...`
|
||||||
const floor = Math.ceil(this.selectedRowKeys.length / 6)
|
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||||
for (let i = 0; i < floor; i++) {
|
await deleteCI(this.selectedRowKeys[i], false)
|
||||||
const itemList = this.selectedRowKeys.slice(6 * i, 6 * i + 6)
|
.then(() => {
|
||||||
const promises = itemList.map((x) => deleteCI(x, false))
|
successNum += 1
|
||||||
await Promise.allSettled(promises)
|
})
|
||||||
.then((res) => {
|
.catch(() => {
|
||||||
res.forEach((r) => {
|
errorNum += 1
|
||||||
if (r.status === 'fulfilled') {
|
|
||||||
successNum += 1
|
|
||||||
} else {
|
|
||||||
errorNum += 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loadTip = `正在删除,共${this.selectedRowKeys.length}个,成功${successNum}个,失败${errorNum}个`
|
this.loadTip = `正在删除,共${this.selectedRowKeys.length}个,成功${successNum}个,失败${errorNum}个`
|
||||||
@@ -891,10 +873,6 @@ export default {
|
|||||||
unsubscribe(ciType, type = 'all') {
|
unsubscribe(ciType, type = 'all') {
|
||||||
const promises = [subscribeCIType(this.typeId, ''), subscribeTreeView(this.typeId, '')]
|
const promises = [subscribeCIType(this.typeId, ''), subscribeTreeView(this.typeId, '')]
|
||||||
Promise.all(promises).then(() => {
|
Promise.all(promises).then(() => {
|
||||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
|
||||||
if (Number(ciType) === Number(lastTypeId)) {
|
|
||||||
localStorage.setItem('ops_ci_typeid', '')
|
|
||||||
}
|
|
||||||
this.$message.success('取消订阅成功')
|
this.$message.success('取消订阅成功')
|
||||||
this.resetRoute()
|
this.resetRoute()
|
||||||
this.$router.push('/cmdb/preference')
|
this.$router.push('/cmdb/preference')
|
||||||
|
@@ -122,12 +122,12 @@
|
|||||||
<a-button type="primary" ghost icon="plus" @click="handleAdd">新增修改字段</a-button>
|
<a-button type="primary" ghost icon="plus" @click="handleAdd">新增修改字段</a-button>
|
||||||
</a-form>
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- </a-form> -->
|
||||||
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
|
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
|
||||||
</CustomDrawer>
|
</CustomDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { Select, Option } from 'element-ui'
|
import { Select, Option } from 'element-ui'
|
||||||
import { getCIType, getCITypeGroupById } from '@/modules/cmdb/api/CIType'
|
import { getCIType, getCITypeGroupById } from '@/modules/cmdb/api/CIType'
|
||||||
@@ -135,7 +135,7 @@ import { addCI } from '@/modules/cmdb/api/ci'
|
|||||||
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
|
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
|
||||||
import { valueTypeMap } from '../../../utils/const'
|
import { valueTypeMap } from '../../../utils/const'
|
||||||
import CreateInstanceFormByGroup from './createInstanceFormByGroup.vue'
|
import CreateInstanceFormByGroup from './createInstanceFormByGroup.vue'
|
||||||
import { getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
import { getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CreateInstanceForm',
|
name: 'CreateInstanceForm',
|
||||||
@@ -166,7 +166,6 @@ export default {
|
|||||||
attributesByGroup: [],
|
attributesByGroup: [],
|
||||||
parentsType: [],
|
parentsType: [],
|
||||||
parentsForm: {},
|
parentsForm: {},
|
||||||
canEdit: {},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -301,16 +300,8 @@ export default {
|
|||||||
this.batchUpdateLists = [{ name: this.attributeList[0].name }]
|
this.batchUpdateLists = [{ name: this.attributeList[0].name }]
|
||||||
})
|
})
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
getCITypeParent(this.typeId).then(async (res) => {
|
getCITypeParent(this.typeId).then((res) => {
|
||||||
for (let i = 0; i < res.parents.length; i++) {
|
this.parentsType = res.parents
|
||||||
await getCanEditByParentIdChildId(res.parents[i].id, this.typeId).then((p_res) => {
|
|
||||||
this.canEdit = {
|
|
||||||
..._.cloneDeep(this.canEdit),
|
|
||||||
[res.parents[i].id]: p_res.result,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.parentsType = res.parents.filter((parent) => this.canEdit[parent.id])
|
|
||||||
const _parentsForm = {}
|
const _parentsForm = {}
|
||||||
res.parents.forEach((item) => {
|
res.parents.forEach((item) => {
|
||||||
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
||||||
|
@@ -59,9 +59,6 @@
|
|||||||
{{ ci[attr.name] }}
|
{{ ci[attr.name] }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="attr.is_list">
|
|
||||||
<span> {{ ci[attr.name].join(',') }}</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>{{ getName(ci[attr.name]) }}</template>
|
<template v-else>{{ getName(ci[attr.name]) }}</template>
|
||||||
</span>
|
</span>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -78,6 +75,7 @@
|
|||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
v-if="attr.is_choice"
|
v-if="attr.is_choice"
|
||||||
:mode="attr.is_list ? 'multiple' : 'default'"
|
:mode="attr.is_list ? 'multiple' : 'default'"
|
||||||
|
:multiple="attr.is_list"
|
||||||
showSearch
|
showSearch
|
||||||
allowClear
|
allowClear
|
||||||
size="small"
|
size="small"
|
||||||
@@ -105,23 +103,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select
|
|
||||||
:style="{ width: '100%' }"
|
|
||||||
v-decorator="[
|
|
||||||
attr.name,
|
|
||||||
{
|
|
||||||
rules: [{ required: attr.is_required }],
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
placeholder="请选择"
|
|
||||||
v-else-if="attr.is_list"
|
|
||||||
mode="tags"
|
|
||||||
showSearch
|
|
||||||
allowClear
|
|
||||||
size="small"
|
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
|
||||||
>
|
|
||||||
</a-select>
|
|
||||||
<a-input-number
|
<a-input-number
|
||||||
size="small"
|
size="small"
|
||||||
v-decorator="[
|
v-decorator="[
|
||||||
@@ -241,7 +222,7 @@ export default {
|
|||||||
this.$nextTick(async () => {
|
this.$nextTick(async () => {
|
||||||
if (this.attr.is_list && !this.attr.is_choice) {
|
if (this.attr.is_list && !this.attr.is_choice) {
|
||||||
this.form.setFieldsValue({
|
this.form.setFieldsValue({
|
||||||
[`${this.attr.name}`]: this.ci[this.attr.name] || null,
|
[`${this.attr.name}`]: this.ci[this.attr.name].join(',') || null,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
<div class="ci-detail-relation-table-title">
|
<div class="ci-detail-relation-table-title">
|
||||||
{{ parent.alias || parent.name }}
|
{{ parent.alias || parent.name }}
|
||||||
<a
|
<a
|
||||||
:disabled="!canEdit[parent.id]"
|
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
$refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, parent.id, 'parents')
|
$refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, parent.id, 'parents')
|
||||||
@@ -24,7 +23,6 @@
|
|||||||
><a-icon
|
><a-icon
|
||||||
type="plus-square"
|
type="plus-square"
|
||||||
/></a>
|
/></a>
|
||||||
<span v-if="!canEdit[parent.id]">(当前模型关系为多对多,请前往关系视图进行增删操作)</span>
|
|
||||||
</div>
|
</div>
|
||||||
<vxe-grid
|
<vxe-grid
|
||||||
v-if="firstCIs[parent.name]"
|
v-if="firstCIs[parent.name]"
|
||||||
@@ -40,14 +38,7 @@
|
|||||||
>
|
>
|
||||||
<template #operation_default="{ row }">
|
<template #operation_default="{ row }">
|
||||||
<a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(row._id, ciId)">
|
<a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(row._id, ciId)">
|
||||||
<a
|
<a :style="{ color: 'red' }"><a-icon type="delete"/></a>
|
||||||
:disabled="!canEdit[parent.id]"
|
|
||||||
:style="{
|
|
||||||
color: !canEdit[parent.id] ? 'rgba(0, 0, 0, 0.25)' : 'red',
|
|
||||||
}"
|
|
||||||
><a-icon
|
|
||||||
type="delete"
|
|
||||||
/></a>
|
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</template>
|
</template>
|
||||||
</vxe-grid>
|
</vxe-grid>
|
||||||
@@ -59,7 +50,6 @@
|
|||||||
<div class="ci-detail-relation-table-title">
|
<div class="ci-detail-relation-table-title">
|
||||||
{{ child.alias || child.name }}
|
{{ child.alias || child.name }}
|
||||||
<a
|
<a
|
||||||
:disabled="!canEdit[child.id]"
|
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
$refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, child.id, 'children')
|
$refs.addTableModal.openModal({ [`${ci.unique}`]: ci[ci.unique] }, ci._id, child.id, 'children')
|
||||||
@@ -68,7 +58,6 @@
|
|||||||
><a-icon
|
><a-icon
|
||||||
type="plus-square"
|
type="plus-square"
|
||||||
/></a>
|
/></a>
|
||||||
<span v-if="!canEdit[child.id]">(当前模型关系为多对多,请前往关系视图进行增删操作)</span>
|
|
||||||
</div>
|
</div>
|
||||||
<vxe-grid
|
<vxe-grid
|
||||||
v-if="secondCIs[child.name]"
|
v-if="secondCIs[child.name]"
|
||||||
@@ -83,14 +72,7 @@
|
|||||||
>
|
>
|
||||||
<template #operation_default="{ row }">
|
<template #operation_default="{ row }">
|
||||||
<a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(ciId, row._id)">
|
<a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(ciId, row._id)">
|
||||||
<a
|
<a :style="{ color: 'red' }"><a-icon type="delete"/></a>
|
||||||
:disabled="!canEdit[child.id]"
|
|
||||||
:style="{
|
|
||||||
color: !canEdit[child.id] ? 'rgba(0, 0, 0, 0.25)' : 'red',
|
|
||||||
}"
|
|
||||||
><a-icon
|
|
||||||
type="delete"
|
|
||||||
/></a>
|
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</template>
|
</template>
|
||||||
</vxe-grid>
|
</vxe-grid>
|
||||||
@@ -103,7 +85,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { getCITypeChildren, getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
import { getCITypeChildren, getCITypeParent } from '@/modules/cmdb/api/CITypeRelation'
|
||||||
import { searchCIRelation, deleteCIRelationView } from '@/modules/cmdb/api/CIRelation'
|
import { searchCIRelation, deleteCIRelationView } from '@/modules/cmdb/api/CIRelation'
|
||||||
import CiDetailRelationTopo from './ciDetailRelationTopo/index.vue'
|
import CiDetailRelationTopo from './ciDetailRelationTopo/index.vue'
|
||||||
import Node from './ciDetailRelationTopo/node.js'
|
import Node from './ciDetailRelationTopo/node.js'
|
||||||
@@ -136,7 +118,6 @@ export default {
|
|||||||
secondCIColumns: {},
|
secondCIColumns: {},
|
||||||
firstCIJsonAttr: {},
|
firstCIJsonAttr: {},
|
||||||
secondCIJsonAttr: {},
|
secondCIJsonAttr: {},
|
||||||
canEdit: {},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -312,85 +293,76 @@ export default {
|
|||||||
.catch((e) => {})
|
.catch((e) => {})
|
||||||
},
|
},
|
||||||
async getParentCITypes() {
|
async getParentCITypes() {
|
||||||
const res = await getCITypeParent(this.typeId)
|
await getCITypeParent(this.typeId)
|
||||||
this.parentCITypes = res.parents
|
.then((res) => {
|
||||||
for (let i = 0; i < res.parents.length; i++) {
|
this.parentCITypes = res.parents
|
||||||
await getCanEditByParentIdChildId(res.parents[i].id, this.typeId).then((p_res) => {
|
|
||||||
this.canEdit = {
|
|
||||||
..._.cloneDeep(this.canEdit),
|
|
||||||
[res.parents[i].id]: p_res.result,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const firstCIColumns = {}
|
|
||||||
const firstCIJsonAttr = {}
|
|
||||||
res.parents.forEach((item) => {
|
|
||||||
const columns = []
|
|
||||||
const jsonAttr = []
|
|
||||||
item.attributes.forEach((attr) => {
|
|
||||||
columns.push({ key: 'p_' + attr.id, field: attr.name, title: attr.alias, minWidth: '100px' })
|
|
||||||
if (attr.value_type === '6') {
|
|
||||||
jsonAttr.push(attr.name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
firstCIJsonAttr[item.id] = jsonAttr
|
|
||||||
firstCIColumns[item.id] = columns
|
|
||||||
firstCIColumns[item.id].push({
|
|
||||||
key: 'p_operation',
|
|
||||||
field: 'operation',
|
|
||||||
title: '操作',
|
|
||||||
width: '60px',
|
|
||||||
fixed: 'right',
|
|
||||||
slots: {
|
|
||||||
default: 'operation_default',
|
|
||||||
},
|
|
||||||
align: 'center',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
this.firstCIColumns = firstCIColumns
|
const firstCIColumns = {}
|
||||||
this.firstCIJsonAttr = firstCIJsonAttr
|
const firstCIJsonAttr = {}
|
||||||
|
res.parents.forEach((item) => {
|
||||||
|
const columns = []
|
||||||
|
const jsonAttr = []
|
||||||
|
item.attributes.forEach((attr) => {
|
||||||
|
columns.push({ key: 'p_' + attr.id, field: attr.name, title: attr.alias, minWidth: '100px' })
|
||||||
|
if (attr.value_type === '6') {
|
||||||
|
jsonAttr.push(attr.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
firstCIJsonAttr[item.id] = jsonAttr
|
||||||
|
firstCIColumns[item.id] = columns
|
||||||
|
firstCIColumns[item.id].push({
|
||||||
|
key: 'p_operation',
|
||||||
|
field: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
width: '60px',
|
||||||
|
fixed: 'right',
|
||||||
|
slots: {
|
||||||
|
default: 'operation_default',
|
||||||
|
},
|
||||||
|
align: 'center',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.firstCIColumns = firstCIColumns
|
||||||
|
this.firstCIJsonAttr = firstCIJsonAttr
|
||||||
|
})
|
||||||
|
.catch((e) => {})
|
||||||
},
|
},
|
||||||
async getChildCITypes() {
|
async getChildCITypes() {
|
||||||
const res = await getCITypeChildren(this.typeId)
|
await getCITypeChildren(this.typeId)
|
||||||
|
.then((res) => {
|
||||||
|
this.childCITypes = res.children
|
||||||
|
|
||||||
this.childCITypes = res.children
|
const secondCIColumns = {}
|
||||||
for (let i = 0; i < res.children.length; i++) {
|
const secondCIJsonAttr = {}
|
||||||
await getCanEditByParentIdChildId(this.typeId, res.children[i].id).then((c_res) => {
|
res.children.forEach((item) => {
|
||||||
this.canEdit = {
|
const columns = []
|
||||||
..._.cloneDeep(this.canEdit),
|
const jsonAttr = []
|
||||||
[res.children[i].id]: c_res.result,
|
item.attributes.forEach((attr) => {
|
||||||
}
|
columns.push({ key: 'c_' + attr.id, field: attr.name, title: attr.alias, minWidth: '100px' })
|
||||||
})
|
if (attr.value_type === '6') {
|
||||||
}
|
jsonAttr.push(attr.name)
|
||||||
const secondCIColumns = {}
|
}
|
||||||
const secondCIJsonAttr = {}
|
})
|
||||||
res.children.forEach((item) => {
|
secondCIJsonAttr[item.id] = jsonAttr
|
||||||
const columns = []
|
secondCIColumns[item.id] = columns
|
||||||
const jsonAttr = []
|
secondCIColumns[item.id].push({
|
||||||
item.attributes.forEach((attr) => {
|
key: 'c_operation',
|
||||||
columns.push({ key: 'c_' + attr.id, field: attr.name, title: attr.alias, minWidth: '100px' })
|
field: 'operation',
|
||||||
if (attr.value_type === '6') {
|
title: '操作',
|
||||||
jsonAttr.push(attr.name)
|
width: '60px',
|
||||||
}
|
fixed: 'right',
|
||||||
})
|
slots: {
|
||||||
secondCIJsonAttr[item.id] = jsonAttr
|
default: 'operation_default',
|
||||||
secondCIColumns[item.id] = columns
|
},
|
||||||
secondCIColumns[item.id].push({
|
align: 'center',
|
||||||
key: 'c_operation',
|
})
|
||||||
field: 'operation',
|
})
|
||||||
title: '操作',
|
|
||||||
width: '60px',
|
|
||||||
fixed: 'right',
|
|
||||||
slots: {
|
|
||||||
default: 'operation_default',
|
|
||||||
},
|
|
||||||
align: 'center',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
this.secondCIColumns = secondCIColumns
|
this.secondCIColumns = secondCIColumns
|
||||||
this.secondCIJsonAttr = secondCIJsonAttr
|
this.secondCIJsonAttr = secondCIJsonAttr
|
||||||
|
})
|
||||||
|
.catch((e) => {})
|
||||||
},
|
},
|
||||||
reload() {
|
reload() {
|
||||||
this.init()
|
this.init()
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
v-if="attr.is_choice"
|
v-if="attr.is_choice"
|
||||||
:mode="attr.is_list ? 'multiple' : 'default'"
|
:mode="attr.is_list ? 'multiple' : 'default'"
|
||||||
|
:multiple="attr.is_list"
|
||||||
showSearch
|
showSearch
|
||||||
allowClear
|
allowClear
|
||||||
>
|
>
|
||||||
|
@@ -53,8 +53,8 @@ export default {
|
|||||||
return postCITypeDiscovery(this.CITypeId, { adr_id: id, interval: type === 'agent' ? 300 : 3600 })
|
return postCITypeDiscovery(this.CITypeId, { adr_id: id, interval: type === 'agent' ? 300 : 3600 })
|
||||||
})
|
})
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
this.getCITypeDiscovery(res[0].id)
|
this.getCITypeDiscovery(this.selectedIds[0].id)
|
||||||
this.$message.success('添加成功')
|
this.$message.success('添加成功')
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
@@ -2,22 +2,20 @@
|
|||||||
<div class="attr-ad" :style="{ height: `${windowHeight - 104}px` }">
|
<div class="attr-ad" :style="{ height: `${windowHeight - 104}px` }">
|
||||||
<div v-if="adCITypeList && adCITypeList.length">
|
<div v-if="adCITypeList && adCITypeList.length">
|
||||||
<a-tabs size="small" v-model="currentTab">
|
<a-tabs size="small" v-model="currentTab">
|
||||||
<a-tab-pane v-for="item in adCITypeList" :key="item.id">
|
<a-tab-pane v-for="item in adCITypeList" :key="item.adr_id">
|
||||||
<a-space slot="tab">
|
<a-space slot="tab">
|
||||||
<span v-if="item.extra_option && item.extra_option.alias">{{ item.extra_option.alias }}</span>
|
<span>{{ getADCITypeParam(item.adr_id) }}</span>
|
||||||
<span v-else>{{ getADCITypeParam(item.adr_id) }}</span>
|
|
||||||
<a-icon type="close-circle" @click="(e) => deleteADT(e, item)" />
|
<a-icon type="close-circle" @click="(e) => deleteADT(e, item)" />
|
||||||
</a-space>
|
</a-space>
|
||||||
<AttrADTabpane
|
<AttrADTabpane
|
||||||
:ref="`attrAdTabpane_${item.id}`"
|
:ref="`attrAdTabpane_${item.adr_id}`"
|
||||||
:adr_id="item.adr_id"
|
:currentTab="item.adr_id"
|
||||||
:adrList="adrList"
|
:adrList="adrList"
|
||||||
:adCITypeList="adCITypeList"
|
:adCITypeList="adCITypeList"
|
||||||
:currentAdt="item"
|
:currentAdt="item"
|
||||||
:ciTypeAttributes="ciTypeAttributes"
|
:ciTypeAttributes="ciTypeAttributes"
|
||||||
:currentAdr="getADCITypeParam(item.adr_id, undefined, true)"
|
:currentAdr="getADCITypeParam(item.adr_id, undefined, true)"
|
||||||
@openEditDrawer="(data, type, adType) => openEditDrawer(data, type, adType)"
|
@openEditDrawer="(data, type, adType) => openEditDrawer(data, type, adType)"
|
||||||
@handleSave="getCITypeDiscovery"
|
|
||||||
/>
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-space
|
<a-space
|
||||||
@@ -137,7 +135,7 @@ export default {
|
|||||||
await getCITypeDiscovery(this.CITypeId).then((res) => {
|
await getCITypeDiscovery(this.CITypeId).then((res) => {
|
||||||
this.adCITypeList = res.filter((item) => item.adr_id)
|
this.adCITypeList = res.filter((item) => item.adr_id)
|
||||||
if (res && res.length && !this.currentTab) {
|
if (res && res.length && !this.currentTab) {
|
||||||
this.currentTab = res[0].id
|
this.currentTab = res[0].adr_id
|
||||||
}
|
}
|
||||||
if (currentTab) {
|
if (currentTab) {
|
||||||
this.currentTab = currentTab
|
this.currentTab = currentTab
|
||||||
@@ -158,7 +156,7 @@ export default {
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
const that = this
|
const that = this
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: `确认删除 【${item?.extra_option?.alias || this.getADCITypeParam(item.adr_id)}】`,
|
title: `确认删除 【${this.getADCITypeParam(item.adr_id)}】`,
|
||||||
content: (h) => (
|
content: (h) => (
|
||||||
<div>
|
<div>
|
||||||
<a-checkbox v-model={that.deletePlugin}>删除插件</a-checkbox>
|
<a-checkbox v-model={that.deletePlugin}>删除插件</a-checkbox>
|
||||||
@@ -166,22 +164,18 @@ export default {
|
|||||||
),
|
),
|
||||||
onOk() {
|
onOk() {
|
||||||
deleteCITypeDiscovery(item.id).then(async () => {
|
deleteCITypeDiscovery(item.id).then(async () => {
|
||||||
if (that.currentTab === item.id) {
|
if (that.currentTab === item.adr_id) {
|
||||||
that.currentTab = ''
|
that.currentTab = ''
|
||||||
}
|
}
|
||||||
|
that.deletePlugin = false
|
||||||
that.$message.success('删除成功!')
|
that.$message.success('删除成功!')
|
||||||
that.getCITypeDiscovery()
|
that.getCITypeDiscovery()
|
||||||
if (that.deletePlugin) {
|
if (that.deletePlugin) {
|
||||||
await deleteDiscovery(item.adr_id).finally(() => {
|
await deleteDiscovery(item.adr_id)
|
||||||
that.deletePlugin = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
that.deletePlugin = false
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onCancel() {
|
onCancel() {},
|
||||||
that.deletePlugin = false
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openEditDrawer(data, type, adType) {
|
openEditDrawer(data, type, adType) {
|
||||||
@@ -189,12 +183,12 @@ export default {
|
|||||||
},
|
},
|
||||||
async updateNotInner(adr) {
|
async updateNotInner(adr) {
|
||||||
const _idx = this.adCITypeList.findIndex((item) => item.adr_id === adr.id)
|
const _idx = this.adCITypeList.findIndex((item) => item.adr_id === adr.id)
|
||||||
let res
|
|
||||||
if (_idx < 0) {
|
if (_idx < 0) {
|
||||||
res = await postCITypeDiscovery(this.CITypeId, { adr_id: adr.id, interval: 300 })
|
await postCITypeDiscovery(this.CITypeId, { adr_id: adr.id, interval: 300 })
|
||||||
}
|
}
|
||||||
await this.getDiscovery()
|
await this.getDiscovery()
|
||||||
await this.getCITypeDiscovery(res?.id ?? undefined)
|
await this.getCITypeDiscovery()
|
||||||
|
this.currentTab = adr.id
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs[`attrAdTabpane_${this.currentTab}`][0].init()
|
this.$refs[`attrAdTabpane_${this.currentTab}`][0].init()
|
||||||
})
|
})
|
||||||
|
@@ -14,7 +14,6 @@
|
|||||||
<span>编辑</span>
|
<span>编辑</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a>
|
</a>
|
||||||
<div>别名:<a-input v-model="alias" style="width:200px;" /></div>
|
|
||||||
<div class="attr-ad-header">字段映射</div>
|
<div class="attr-ad-header">字段映射</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
v-if="adrType === 'agent'"
|
v-if="adrType === 'agent'"
|
||||||
@@ -57,7 +56,7 @@
|
|||||||
:ruleName="adrName"
|
:ruleName="adrName"
|
||||||
:ciTypeAttributes="ciTypeAttributes"
|
:ciTypeAttributes="ciTypeAttributes"
|
||||||
:adCITypeList="adCITypeList"
|
:adCITypeList="adCITypeList"
|
||||||
:currentTab="adr_id"
|
:currentTab="currentTab"
|
||||||
:style="{ marginBottom: '20px' }"
|
:style="{ marginBottom: '20px' }"
|
||||||
/>
|
/>
|
||||||
<a-form-model
|
<a-form-model
|
||||||
@@ -134,7 +133,7 @@ export default {
|
|||||||
name: 'AttrADTabpane',
|
name: 'AttrADTabpane',
|
||||||
components: { Vcrontab, HttpSnmpAD, CMDBExprDrawer, MonitorNodeSetting },
|
components: { Vcrontab, HttpSnmpAD, CMDBExprDrawer, MonitorNodeSetting },
|
||||||
props: {
|
props: {
|
||||||
adr_id: {
|
currentTab: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
@@ -188,7 +187,6 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
form3: this.$form.createForm(this, { name: 'snmp_form' }),
|
form3: this.$form.createForm(this, { name: 'snmp_form' }),
|
||||||
alias: '',
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -207,7 +205,7 @@ export default {
|
|||||||
},
|
},
|
||||||
agentTypeRadioList() {
|
agentTypeRadioList() {
|
||||||
const { permissions = [] } = this.userRoles
|
const { permissions = [] } = this.userRoles
|
||||||
if ((permissions.includes('cmdb_admin') || permissions.includes('admin')) && this.adrType !== 'http') {
|
if (permissions.includes('cmdb_admin') || permissions.includes('admin')) {
|
||||||
return [
|
return [
|
||||||
{ value: 'all', label: '所有节点' },
|
{ value: 'all', label: '所有节点' },
|
||||||
{ value: 'agent_id', label: '指定节点' },
|
{ value: 'agent_id', label: '指定节点' },
|
||||||
@@ -223,9 +221,8 @@ export default {
|
|||||||
mounted() {},
|
mounted() {},
|
||||||
methods: {
|
methods: {
|
||||||
init() {
|
init() {
|
||||||
const _find = this.adrList.find((item) => Number(item.id) === Number(this.adr_id))
|
const _find = this.adrList.find((item) => Number(item.id) === Number(this.currentTab))
|
||||||
const _findADT = this.adCITypeList.find((item) => Number(item.id) === Number(this.currentAdt.id))
|
const _findADT = this.adCITypeList.find((item) => Number(item.adr_id) === Number(this.currentTab))
|
||||||
this.alias = _findADT?.extra_option?.alias ?? ''
|
|
||||||
if (this.adrType === 'http') {
|
if (this.adrType === 'http') {
|
||||||
const { category = undefined, key = '', secret = '' } = _findADT?.extra_option ?? {}
|
const { category = undefined, key = '', secret = '' } = _findADT?.extra_option ?? {}
|
||||||
this.form2 = {
|
this.form2 = {
|
||||||
@@ -297,7 +294,7 @@ export default {
|
|||||||
this.cron = cron
|
this.cron = cron
|
||||||
},
|
},
|
||||||
handleSave() {
|
handleSave() {
|
||||||
const { currentAdt, alias } = this
|
const { currentAdt } = this
|
||||||
let params
|
let params
|
||||||
if (this.adrType === 'http') {
|
if (this.adrType === 'http') {
|
||||||
params = {
|
params = {
|
||||||
@@ -363,15 +360,9 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (params.extra_option) {
|
|
||||||
params.extra_option.alias = alias
|
|
||||||
} else {
|
|
||||||
params.extra_option = {}
|
|
||||||
params.extra_option.alias = alias
|
|
||||||
}
|
|
||||||
putCITypeDiscovery(currentAdt.id, params).then((res) => {
|
putCITypeDiscovery(currentAdt.id, params).then((res) => {
|
||||||
this.$message.success('保存成功')
|
this.$message.success('保存成功')
|
||||||
this.$emit('handleSave')
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleOpenCmdb() {
|
handleOpenCmdb() {
|
||||||
|
@@ -372,7 +372,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async open(property, attrList) {
|
async open(property, attrList) {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
await this.getNoticeConfigAppBot()
|
this.getNoticeConfigAppBot()
|
||||||
this.attrList = attrList
|
this.attrList = attrList
|
||||||
if (property.has_trigger) {
|
if (property.has_trigger) {
|
||||||
this.triggerId = property.trigger.id
|
this.triggerId = property.trigger.id
|
||||||
|
@@ -372,7 +372,7 @@ export default {
|
|||||||
width: 3,
|
width: 3,
|
||||||
fontColor: '#ffffff',
|
fontColor: '#ffffff',
|
||||||
bgColor: ['#6ABFFE', '#5375EB'],
|
bgColor: ['#6ABFFE', '#5375EB'],
|
||||||
chartColor: '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF', // 图表颜色
|
chartColor: '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD', // 图表颜色
|
||||||
isShowPreview: false,
|
isShowPreview: false,
|
||||||
filterExp: undefined,
|
filterExp: undefined,
|
||||||
previewData: null,
|
previewData: null,
|
||||||
@@ -410,7 +410,7 @@ export default {
|
|||||||
this.width = width
|
this.width = width
|
||||||
this.chartType = chartType
|
this.chartType = chartType
|
||||||
this.filterExp = item?.options?.filter ?? ''
|
this.filterExp = item?.options?.filter ?? ''
|
||||||
this.chartColor = item?.options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF'
|
this.chartColor = item?.options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD'
|
||||||
this.isShadow = item?.options?.isShadow ?? false
|
this.isShadow = item?.options?.isShadow ?? false
|
||||||
|
|
||||||
if (chartType === 'count') {
|
if (chartType === 'count') {
|
||||||
|
@@ -24,7 +24,7 @@ export const category_1_bar_options = (data, options) => {
|
|||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
|
||||||
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','),
|
color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
|
||||||
grid: {
|
grid: {
|
||||||
top: 15,
|
top: 15,
|
||||||
left: 'left',
|
left: 'left',
|
||||||
@@ -83,7 +83,7 @@ export const category_1_bar_options = (data, options) => {
|
|||||||
export const category_1_line_options = (data, options) => {
|
export const category_1_line_options = (data, options) => {
|
||||||
const xData = Object.keys(data)
|
const xData = Object.keys(data)
|
||||||
return {
|
return {
|
||||||
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','),
|
color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
|
||||||
grid: {
|
grid: {
|
||||||
top: 15,
|
top: 15,
|
||||||
left: 'left',
|
left: 'left',
|
||||||
@@ -117,7 +117,7 @@ export const category_1_line_options = (data, options) => {
|
|||||||
x2: 0,
|
x2: 0,
|
||||||
y2: 1,
|
y2: 1,
|
||||||
colorStops: [{
|
colorStops: [{
|
||||||
offset: 0, color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(',')[0] // 0% 处的颜色
|
offset: 0, color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(',')[0] // 0% 处的颜色
|
||||||
}, {
|
}, {
|
||||||
offset: 1, color: '#ffffff' // 100% 处的颜色
|
offset: 1, color: '#ffffff' // 100% 处的颜色
|
||||||
}],
|
}],
|
||||||
@@ -131,7 +131,7 @@ export const category_1_line_options = (data, options) => {
|
|||||||
|
|
||||||
export const category_1_pie_options = (data, options) => {
|
export const category_1_pie_options = (data, options) => {
|
||||||
return {
|
return {
|
||||||
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','),
|
color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
|
||||||
grid: {
|
grid: {
|
||||||
top: 10,
|
top: 10,
|
||||||
left: 'left',
|
left: 'left',
|
||||||
@@ -181,7 +181,7 @@ export const category_2_bar_options = (data, options, chartType) => {
|
|||||||
})
|
})
|
||||||
const legend = [...new Set(_legend)]
|
const legend = [...new Set(_legend)]
|
||||||
return {
|
return {
|
||||||
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','),
|
color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
|
||||||
grid: {
|
grid: {
|
||||||
top: 15,
|
top: 15,
|
||||||
left: 'left',
|
left: 'left',
|
||||||
@@ -249,7 +249,7 @@ export const category_2_bar_options = (data, options, chartType) => {
|
|||||||
x2: 0,
|
x2: 0,
|
||||||
y2: 1,
|
y2: 1,
|
||||||
colorStops: [{
|
colorStops: [{
|
||||||
offset: 0, color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(',')[index % 8] // 0% 处的颜色
|
offset: 0, color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(',')[index % 8] // 0% 处的颜色
|
||||||
}, {
|
}, {
|
||||||
offset: 1, color: '#ffffff' // 100% 处的颜色
|
offset: 1, color: '#ffffff' // 100% 处的颜色
|
||||||
}],
|
}],
|
||||||
@@ -269,7 +269,7 @@ export const category_2_pie_options = (data, options) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
color: (options?.chartColor ?? '#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF').split(','),
|
color: (options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD').split(','),
|
||||||
grid: {
|
grid: {
|
||||||
top: 15,
|
top: 15,
|
||||||
left: 'left',
|
left: 'left',
|
||||||
|
@@ -24,8 +24,10 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
list: [
|
list: [
|
||||||
'#5DADF2,#86DFB7,#5A6F96,#7BD5FF,#FFB980,#4D58D6,#D9B6E9,#8054FF',
|
'#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD',
|
||||||
'#9BA1F9,#0F2BA8,#A2EBFE,#4982F6,#FEB09C,#6C78E8,#FFDDAB,#4D66BD',
|
'#C1A9DC,#E2B5CD,#EE8EBC,#8483C3,#4D66BD,#213764,#D9B6E9,#DD88EB',
|
||||||
|
'#6FC4DF,#9FE8CE,#16B4BE,#86E6FB,#1871A3,#E1BF8D,#ED8D8D,#DD88EB',
|
||||||
|
'#F8B751,#FC9054,#FFE380,#DF963F,#AB5200,#EA9387,#FFBB7C,#D27467',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -61,7 +61,7 @@
|
|||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column field="type_id" title="模型" width="150px">
|
<vxe-column field="type_id" title="模型" width="150px">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ row.operate_type === '删除模型' ? row.change.alias : row.type_id }}
|
{{ row.operate_type === '删除模型' ? row.change.alias : row.type_id}}
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column field="changeDescription" title="描述">
|
<vxe-column field="changeDescription" title="描述">
|
||||||
|
@@ -314,12 +314,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(promises).then(() => {
|
Promise.all(promises).then(() => {
|
||||||
if (type === 'all' || type === 'ci') {
|
|
||||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
|
||||||
if (Number(ciType.id) === Number(lastTypeId)) {
|
|
||||||
localStorage.setItem('ops_ci_typeid', '')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
that.$message.success('取消订阅成功')
|
that.$message.success('取消订阅成功')
|
||||||
that.resetRoute()
|
that.resetRoute()
|
||||||
})
|
})
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user