├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── README.zh-CN.md ├── api_basebone ├── __init__.py ├── admin.py ├── app │ ├── __init__.py │ ├── account │ │ ├── __init__.py │ │ ├── forms.py │ │ └── views.py │ ├── client_urls.py │ ├── manage_urls.py │ └── upload │ │ ├── __init__.py │ │ └── views.py ├── apps.py ├── bsm │ ├── __init__.py │ ├── admin.py │ ├── api.py │ └── functions │ │ └── __init__.py ├── const │ ├── __init__.py │ └── field_map.py ├── core │ ├── __init__.py │ ├── admin.py │ ├── const.py │ ├── decorators.py │ ├── drf_field.py │ ├── exceptions.py │ ├── fields.py │ ├── gmeta.py │ ├── object_model.py │ └── widgets.py ├── db │ ├── __init__.py │ └── query.py ├── drf │ ├── __init__.py │ ├── authentication.py │ ├── fields.py │ ├── handler.py │ ├── pagination.py │ ├── permissions.py │ ├── response.py │ └── routers.py ├── export │ ├── __init__.py │ ├── admin.py │ ├── fields.py │ ├── setting.py │ └── specs.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── bsm_api_doc.py │ │ └── bsm_config.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20190524_1155.py │ ├── 0003_api_field_filter_parameter.py │ ├── 0004_auto_20190910_2242.py │ ├── 0005_auto_20190910_2244.py │ ├── 0006_auto_20190910_2246.py │ ├── 0007_auto_20190910_2247.py │ ├── 0008_auto_20190910_2254.py │ ├── 0009_auto_20190911_1547.py │ ├── 0010_filter_layer.py │ ├── 0011_auto_20190924_1142.py │ ├── 0012_auto_20190930_1715.py │ ├── 0013_auto_20191012_1642.py │ ├── 0014_auto_20191014_1518.py │ ├── 0015_parameter_is_array.py │ ├── 0016_auto_20191022_1928.py │ ├── 0017_auto_20191101_1829.py │ ├── 0018_auto_20201225_1704.py │ └── __init__.py ├── models.py ├── permissions.py ├── readme.md ├── restful │ ├── __init__.py │ ├── batch_actions.py │ ├── client │ │ ├── __init__.py │ │ ├── urls.py │ │ ├── user_pip.py │ │ └── views.py │ ├── const.py │ ├── export │ │ ├── __init__.py │ │ ├── excel.py │ │ └── formatter.py │ ├── forms.py │ ├── funcs.py │ ├── manage │ │ ├── __init__.py │ │ ├── config_urls.py │ │ ├── config_views.py │ │ ├── urls.py │ │ ├── user_pip.py │ │ └── views.py │ ├── mixins.py │ ├── relation │ │ ├── __init__.py │ │ └── reverse_m2m.py │ ├── relations.py │ ├── renderers.py │ ├── renderers_v2.py │ ├── serializers.py │ └── viewsets.py ├── sandbox │ ├── __init__.py │ ├── functions.py │ └── logger.py ├── services │ ├── __init__.py │ ├── expresstion.py │ ├── functions.py │ ├── queryset.py │ └── rest_services.py ├── settings.py ├── signals │ └── __init__.py ├── tasks.py ├── templates │ ├── bsm_admin_config.html │ └── bsm_schema_config.html ├── templatetags │ ├── __init__.py │ └── jsonify.py ├── tests │ ├── __init__.py │ ├── test_api.py │ └── utils │ │ └── __init__.py ├── urls.py └── utils │ ├── __init__.py │ ├── aliyun.py │ ├── data.py │ ├── dingding.py │ ├── format.py │ ├── gmeta.py │ ├── meta.py │ ├── module.py │ ├── operators.py │ ├── queryset.py │ ├── redis.py │ ├── retry.py │ ├── sentry.py │ ├── sign.py │ ├── tencent.py │ ├── timezone.py │ ├── upload.py │ ├── uuid.py │ └── wechat │ ├── __init__.py │ ├── extension │ ├── __init__.py │ └── logistics.py │ ├── form_id.py │ ├── logistics.py │ └── template_message.py ├── bsm_config ├── __init__.py ├── admin.py ├── apps.py ├── bsm │ ├── __init__.py │ ├── admin.py │ └── functions.py ├── exceptions.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20190509_2031.py │ ├── 0003_auto_20190509_2125.py │ ├── 0004_auto_20190510_1820.py │ ├── 0005_auto_20190709_1338.py │ ├── 0006_auto_20190712_1658.py │ ├── 0007_auto_20200102_1616.py │ ├── 0008_auto_20200120_1416.py │ ├── 0009_auto_20200226_1311.py │ ├── 0010_auto_20200226_1514.py │ ├── 0011_auto_20200410_2247.py │ ├── 0012_auto_20200417_1847.py │ ├── 0013_auto_20200418_1816.py │ ├── 0014_auto_20200422_1925.py │ ├── 0015_auto_20200423_1231.py │ ├── 0016_auto_20200429_1117.py │ ├── 0017_auto_20200511_1927.py │ ├── 0018_menu_groups.py │ ├── 0019_setting_is_admin.py │ ├── 0020_auto_20200530_1520.py │ ├── 0021_auto_20200531_2305.py │ ├── 0022_auto_20200602_1629.py │ ├── 0023_setting_help_text.py │ ├── 0024_setting_extra_data.py │ ├── 0025_auto_20200716_2253.py │ ├── 0026_auto_20200722_2308.py │ ├── 0027_auto_20200822_2031.py │ ├── 0028_auto_20200925_1216.py │ ├── 0029_auto_20200926_0010.py │ ├── 0030_fieldadmin_fieldpermission.py │ ├── 0031_auto_20201126_1344.py │ ├── 0031_auto_20201203_1222.py │ ├── 0032_auto_20201126_1802.py │ ├── 0032_auto_20201203_0530.py │ ├── 0033_auto_20201203_1558.py │ ├── 0034_merge_20201205_1249.py │ ├── 0035_menu_view.py │ ├── 0036_auto_20210320_1712.py │ └── __init__.py ├── models.py ├── settings.py ├── signals.py ├── site_setting.py ├── site_setting_config.py ├── tests.py ├── utils.py └── views.py ├── lightning ├── __init__.py ├── admin.py ├── apps.py ├── bsm │ ├── __init__.py │ ├── admin.py │ └── user_forms.py ├── const.py ├── decorators.py ├── exceptions.py ├── fields.py ├── form.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── light.py ├── migrations │ └── __init__.py ├── models.py ├── services.py ├── settings.py ├── signals.py ├── static │ └── lightning │ │ ├── 0.fb850fc8.async.js │ │ ├── 1.8ac3e930.async.js │ │ ├── 10.645c8006.chunk.css │ │ ├── 10.eec22bee.async.js │ │ ├── 100.4d31df76.async.js │ │ ├── 101.33c887ed.async.js │ │ ├── 102.f1e1d41f.async.js │ │ ├── 103.9c67c7a7.async.js │ │ ├── 104.25d0123f.async.js │ │ ├── 105.29500b7d.async.js │ │ ├── 106.94cfe61b.async.js │ │ ├── 107.bfc4865f.async.js │ │ ├── 108.d7bd25bb.async.js │ │ ├── 109.8228bd9f.async.js │ │ ├── 11.02926974.async.js │ │ ├── 11.18357312.chunk.css │ │ ├── 110.eae832ea.async.js │ │ ├── 111.07599688.async.js │ │ ├── 112.9411ded5.async.js │ │ ├── 113.637b788d.async.js │ │ ├── 114.cdff58df.async.js │ │ ├── 115.f9bcbae5.async.js │ │ ├── 116.77bb1da1.async.js │ │ ├── 117.b146effd.async.js │ │ ├── 118.70499dd4.async.js │ │ ├── 119.d8b363ed.async.js │ │ ├── 12.4b0d7cbd.async.js │ │ ├── 120.9d033479.async.js │ │ ├── 121.300d72ae.async.js │ │ ├── 122.352eb326.async.js │ │ ├── 123.23dee777.async.js │ │ ├── 124.42ba677d.async.js │ │ ├── 125.e72bc89e.async.js │ │ ├── 126.629299b4.async.js │ │ ├── 127.0792d9bd.async.js │ │ ├── 128.cab961a4.async.js │ │ ├── 129.c31e0048.async.js │ │ ├── 13.05a968af.chunk.css │ │ ├── 13.fff17059.async.js │ │ ├── 130.ba2eeb7a.async.js │ │ ├── 131.d9e04569.async.js │ │ ├── 132.2e857314.async.js │ │ ├── 133.a451f100.async.js │ │ ├── 134.4760c504.async.js │ │ ├── 135.acbb4743.async.js │ │ ├── 136.34736429.async.js │ │ ├── 137.4888aebb.async.js │ │ ├── 138.4f0d13eb.async.js │ │ ├── 139.4e11a356.async.js │ │ ├── 14.3b6e9a13.chunk.css │ │ ├── 14.d4028317.async.js │ │ ├── 140.73eb36e2.async.js │ │ ├── 141.262a13f3.async.js │ │ ├── 142.2a15a553.async.js │ │ ├── 143.7f48a514.async.js │ │ ├── 144.5ef202da.async.js │ │ ├── 145.4f22e9ea.async.js │ │ ├── 146.54995c6d.async.js │ │ ├── 147.aed9ee54.async.js │ │ ├── 148.0a26588c.async.js │ │ ├── 149.b3161487.async.js │ │ ├── 15.772385c8.chunk.css │ │ ├── 15.dff66ec4.async.js │ │ ├── 150.e429bc6c.async.js │ │ ├── 151.cfa1f2a1.async.js │ │ ├── 152.c3eb9fe3.async.js │ │ ├── 153.9c8b2e9c.async.js │ │ ├── 154.c4b3747c.async.js │ │ ├── 155.6a3a2f86.async.js │ │ ├── 156.46553931.async.js │ │ ├── 157.7ac20074.async.js │ │ ├── 158.ec508940.async.js │ │ ├── 159.dcde4d9c.async.js │ │ ├── 16.34dd97c6.async.js │ │ ├── 160.c012b785.async.js │ │ ├── 161.b7a28b01.async.js │ │ ├── 162.815ddc3e.async.js │ │ ├── 163.3aa11615.async.js │ │ ├── 164.697e923e.async.js │ │ ├── 165.29c4f9e6.async.js │ │ ├── 166.b9b3a0b6.async.js │ │ ├── 167.f4bc23e1.async.js │ │ ├── 168.1d6a136d.async.js │ │ ├── 169.1e94901c.async.js │ │ ├── 17.4317dfc4.async.js │ │ ├── 170.1b6ec0a7.async.js │ │ ├── 171.7eeebcb6.async.js │ │ ├── 172.4bb01d6a.async.js │ │ ├── 18.e496b755.async.js │ │ ├── 19.b631c292.async.js │ │ ├── 2.20482c57.chunk.css │ │ ├── 2.b1e44aef.async.js │ │ ├── 20.e21830bb.async.js │ │ ├── 3.a702e33e.chunk.css │ │ ├── 3.d254986d.async.js │ │ ├── 42.90cb5461.chunk.css │ │ ├── 42.c513af2f.async.js │ │ ├── 43.16eb5b29.chunk.css │ │ ├── 43.8356389d.async.js │ │ ├── 44.8895834a.async.js │ │ ├── 45.068cb946.chunk.css │ │ ├── 45.e5f93880.async.js │ │ ├── 46.94b3910a.async.js │ │ ├── 46.bed58943.chunk.css │ │ ├── 47.983aa290.chunk.css │ │ ├── 47.fadbc231.async.js │ │ ├── 48.983aa290.chunk.css │ │ ├── 48.cf235af3.async.js │ │ ├── 49.609cae97.async.js │ │ ├── 5.12e1e653.async.js │ │ ├── 5.d72f5188.chunk.css │ │ ├── 50.049c1c78.chunk.css │ │ ├── 50.69f24eb0.async.js │ │ ├── 51.2b732483.async.js │ │ ├── 51.983aa290.chunk.css │ │ ├── 52.a2b88eb8.chunk.css │ │ ├── 52.ed025460.async.js │ │ ├── 53.107c795b.async.js │ │ ├── 53.a806c168.chunk.css │ │ ├── 54.08e31055.async.js │ │ ├── 55.636f20e3.async.js │ │ ├── 56.79b1d571.async.js │ │ ├── 57.b7869fd9.async.js │ │ ├── 58.3a1bd555.async.js │ │ ├── 59.3298e6c3.async.js │ │ ├── 6.3db1091f.chunk.css │ │ ├── 6.ca282251.async.js │ │ ├── 60.ed64192b.async.js │ │ ├── 61.b963de88.async.js │ │ ├── 62.7aec6ab5.async.js │ │ ├── 63.83003ecb.async.js │ │ ├── 64.6033b0e9.async.js │ │ ├── 65.4597dffc.async.js │ │ ├── 66.aabd54d4.async.js │ │ ├── 67.9d33603c.async.js │ │ ├── 68.90f9ea39.async.js │ │ ├── 69.6d430011.async.js │ │ ├── 7.4fd80dc7.async.js │ │ ├── 70.0fe53a06.async.js │ │ ├── 71.363be263.async.js │ │ ├── 72.02530989.async.js │ │ ├── 73.f7b6c165.async.js │ │ ├── 74.912e3017.async.js │ │ ├── 75.31bb496a.async.js │ │ ├── 76.f8f87284.async.js │ │ ├── 77.45fb932c.async.js │ │ ├── 78.593fdce4.async.js │ │ ├── 79.385f767b.async.js │ │ ├── 80.0d08b6de.async.js │ │ ├── 81.f681ca00.async.js │ │ ├── 82.7adb84e7.async.js │ │ ├── 83.323d014c.async.js │ │ ├── 84.9d92592d.async.js │ │ ├── 85.d116d248.async.js │ │ ├── 86.9ed1e2e1.async.js │ │ ├── 87.7f369964.async.js │ │ ├── 88.199a7b74.async.js │ │ ├── 89.8c525a0c.async.js │ │ ├── 90.0ef510be.async.js │ │ ├── 91.f87553f5.async.js │ │ ├── 92.a01bac0b.async.js │ │ ├── 93.2f7d3fb9.async.js │ │ ├── 94.13c6d25e.async.js │ │ ├── 95.45f71de1.async.js │ │ ├── 96.2db9b769.async.js │ │ ├── 97.9a8a06c9.async.js │ │ ├── 98.9121f030.async.js │ │ ├── 99.6f76ffe3.async.js │ │ ├── AdminConfig.9acc57ba.async.js │ │ ├── CNAME │ │ ├── admin-panel.614324f6.async.js │ │ ├── amap.72a3a5ac.async.js │ │ ├── asset-manifest.json │ │ ├── code-editor.3ed00599.async.js │ │ ├── code-editor.9bf73167.chunk.css │ │ ├── dnd.1c9d98ce.async.js │ │ ├── excel.d8343a7d.async.js │ │ ├── favicon.png │ │ ├── home_bg.png │ │ ├── icons │ │ ├── icon-128x128.png │ │ ├── icon-192x192.png │ │ └── icon-512x512.png │ │ ├── img.08952397.async.js │ │ ├── index.html │ │ ├── installing.d03c439b.chunk.css │ │ ├── installing.daab5d59.async.js │ │ ├── logo.png │ │ ├── p__BSMApp.a4d5c16b.async.js │ │ ├── p__BSMApp.a9c02d11.chunk.css │ │ ├── pro_icon.svg │ │ ├── react-doc-viewer.adb5949f.async.js │ │ ├── rich-text.3e45f05d.async.js │ │ ├── static │ │ ├── BarChart.44321352.png │ │ ├── CharTabel.bf487762.png │ │ ├── Column-lineComboChart.089ebe73.png │ │ ├── CurveStatisticsCard.368c916f.png │ │ ├── Funnelchart.a45b9353.png │ │ ├── LineChart.09df74ac.png │ │ ├── PieChart .6520be22.png │ │ ├── RadarChart.e91c612c.png │ │ ├── logo-enterprise.44e6a2eb.png │ │ ├── logo.3ac90302.png │ │ ├── panda.58d815cb.jpg │ │ └── table.02a8a35d.png │ │ ├── umi.28ea031d.js │ │ ├── umi.e74836a1.css │ │ ├── vendors~amap.10c61a26.async.js │ │ ├── vendors~amap.db0a0295.chunk.css │ │ ├── vendors~code-editor.78cbebf3.async.js │ │ ├── vendors~code-editor.b063c611.chunk.css │ │ ├── vendors~dnd.e48c4778.async.js │ │ ├── vendors~excel.6a4feb13.async.js │ │ ├── vendors~img.97e38e30.async.js │ │ ├── vendors~json-view.b9a645e5.async.js │ │ ├── vendors~p__BSMApp.3c21d343.chunk.css │ │ ├── vendors~p__BSMApp.c292b717.async.js │ │ ├── vendors~p__BSMApp~react-doc-viewer.fc0451a5.async.js │ │ ├── vendors~react-doc-viewer.34b3e51a.async.js │ │ ├── vendors~rich-text.f50f718d.chunk.css │ │ ├── vendors~rich-text.ff9777dc.async.js │ │ ├── vendors~video.925f4123.chunk.css │ │ ├── vendors~video.ec660f72.async.js │ │ ├── video.92647ec0.chunk.css │ │ └── video.ef77bd9a.async.js ├── tests.py ├── urls.py └── views.py ├── lightning_code ├── __init__.py ├── admin.py ├── apps.py ├── bsm │ ├── __init__.py │ └── admin.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20210524_1921.py │ ├── 0003_auto_20210524_1927.py │ ├── 0004_auto_20210526_1206.py │ └── __init__.py ├── models.py ├── tasks.py ├── tests.py └── views.py ├── lightning_flags.py ├── puzzle ├── __init__.py ├── bsm │ ├── __init__.py │ └── admin.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20201125_0722.py │ ├── 0003_auto_20201126_1802.py │ ├── 0004_table.py │ ├── 0005_auto_20210426_1149.py │ ├── 0006_auto_20210524_1927.py │ └── __init__.py ├── models.py ├── requirements.in ├── services.py ├── tests.py ├── urls.py └── views.py ├── setup.cfg ├── setup.py ├── shield ├── __init__.py ├── apps.py ├── bsm │ └── admin.py ├── filter.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_rule_combinator.py │ └── __init__.py ├── models.py └── signals.py └── storage ├── __init__.py ├── urls.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | .vscode 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | 107 | # tests/ 108 | blog/ 109 | member/ 110 | service/ 111 | manage.py 112 | 113 | .idea -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | 3 | ## [1.2.0] 4 | 5 | ### Changed 6 | 7 | - 默认启用了csrf保护 8 | 9 | ## [1.0.2] 10 | 11 | ### Fixed 12 | 13 | - 解决只有一个筛选字段时无法出现查询按钮的问题 14 | - 解决初始化配置后,更新面板点重置再点更新导致筛选字段丢失的问题 15 | 16 | ## [1.0.1] 17 | 18 | ### Added 19 | 20 | - 允许使用`./manage.py collectstatic`的方式部署 21 | - 自动使用`settings.MEDIA_ROOT`作文件上传目录,没设置则默认在当前目录自动建立`lightning-files`子目录用作文件上传 22 | 23 | ## [1.0.0] 24 | 25 | ### Added 26 | 27 | - Lightning正式版 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Gitmen Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include MANIFEST.in 3 | include requirements.in 4 | include *.md 5 | recursive-include lightning/static *.png *.jpg *.css *.js *.html 6 | global-exclude __pycache__ 7 | global-exclude *.py[co] 8 | -------------------------------------------------------------------------------- /api_basebone/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'api_basebone.apps.ApiBaseboneConfig' 2 | -------------------------------------------------------------------------------- /api_basebone/admin.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/admin.py -------------------------------------------------------------------------------- /api_basebone/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/app/__init__.py -------------------------------------------------------------------------------- /api_basebone/app/account/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/app/account/__init__.py -------------------------------------------------------------------------------- /api_basebone/app/account/forms.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import authenticate, login, get_user_model 2 | from django.contrib.auth.hashers import make_password 3 | 4 | from rest_framework import serializers 5 | from rest_framework.exceptions import ValidationError 6 | 7 | User = get_user_model() 8 | 9 | 10 | class LoginForm(serializers.Serializer): 11 | username = serializers.CharField() 12 | password = serializers.CharField() 13 | 14 | def validate(self, data): 15 | request = self.context['request'] 16 | user = authenticate(request, **data) 17 | if not user: 18 | raise ValidationError({'password': '用户不存在或密码不正确'}) 19 | 20 | login(request, user) 21 | return {'user': user} 22 | 23 | def create(self, validated_data): 24 | return validated_data['user'] 25 | 26 | 27 | class UserCreateUpdateForm(serializers.ModelSerializer): 28 | """默认的用户序列化类""" 29 | 30 | class Meta: 31 | model = User 32 | fields = '__all__' 33 | extra_kwargs = { 34 | 'password': { 35 | 'required': False 36 | } 37 | } 38 | 39 | def create(self, validated_data): 40 | """创建""" 41 | validated_data['password'] = make_password(validated_data['password']) 42 | return User.objects.create(**validated_data) 43 | 44 | def update(self, instance, validated_data): 45 | """更新""" 46 | if 'password' in validated_data: 47 | password = validated_data.pop('password') 48 | instance = super().update(instance, validated_data) 49 | instance.set_password(password) 50 | instance.save(update_fields=['password']) 51 | else: 52 | instance = super().update(instance, validated_data) 53 | return instance 54 | -------------------------------------------------------------------------------- /api_basebone/app/client_urls.py: -------------------------------------------------------------------------------- 1 | from api_basebone.drf.routers import SimpleRouter 2 | from .upload import views as upload_views 3 | 4 | 5 | router = SimpleRouter(custom_base_name='basebone-app') 6 | 7 | router.register('upload', upload_views.UploadViewSet) 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /api_basebone/app/manage_urls.py: -------------------------------------------------------------------------------- 1 | from api_basebone.drf.routers import SimpleRouter 2 | from .account.views import ManageAccountViewSet 3 | from .upload import views as upload_views 4 | 5 | 6 | router = SimpleRouter(custom_base_name='basebone-app') 7 | 8 | router.register('account', ManageAccountViewSet) 9 | router.register('upload', upload_views.UploadViewSet) 10 | 11 | urlpatterns = router.urls 12 | -------------------------------------------------------------------------------- /api_basebone/app/upload/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/app/upload/__init__.py -------------------------------------------------------------------------------- /api_basebone/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiBaseboneConfig(AppConfig): 5 | name = 'api_basebone' 6 | 7 | def ready(self): 8 | # import member.bsm.functions.form_id 9 | # from member import signals 10 | from api_basebone.restful.client.views import register_api 11 | from api_basebone.bsm.api import exposed 12 | import api_basebone.bsm.functions # 注册所有云函数 13 | from api_basebone import db 14 | 15 | register_api(self.name, exposed) 16 | -------------------------------------------------------------------------------- /api_basebone/bsm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/bsm/__init__.py -------------------------------------------------------------------------------- /api_basebone/bsm/admin.py: -------------------------------------------------------------------------------- 1 | from api_basebone.core.admin import BSMAdmin, register 2 | from api_basebone.models import AdminLog 3 | 4 | 5 | @register 6 | class UserAdmin(BSMAdmin): 7 | 8 | display = [ 9 | 'action_time', 10 | 'user.username', 11 | 'action', 12 | 'app_label', 13 | 'model_slug', 14 | 'object_id', 15 | 'params', 16 | ] 17 | table_actions = [] 18 | 19 | class Meta: 20 | model = AdminLog 21 | -------------------------------------------------------------------------------- /api_basebone/bsm/api.py: -------------------------------------------------------------------------------- 1 | exposed = {'api': {'actions': ['list', 'set', 'func'], 'permissions': []}} 2 | 3 | -------------------------------------------------------------------------------- /api_basebone/bsm/functions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/bsm/functions/__init__.py -------------------------------------------------------------------------------- /api_basebone/const/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/const/__init__.py -------------------------------------------------------------------------------- /api_basebone/const/field_map.py: -------------------------------------------------------------------------------- 1 | from rest_framework import fields 2 | 3 | from api_basebone.core import drf_field 4 | from api_basebone.export.specs import FieldType 5 | 6 | # 计算字段的类型和序列化字段的映射 7 | ComputedFieldTypeSerializerMap = { 8 | FieldType.STRING: fields.CharField, 9 | FieldType.INTEGER: fields.IntegerField, 10 | FieldType.BOOL: fields.BooleanField, 11 | FieldType.TEXT: fields.CharField, 12 | FieldType.RICHTEXT: fields.CharField, 13 | FieldType.FLOAT: fields.FloatField, 14 | FieldType.DECIMAL: fields.DecimalField, 15 | FieldType.IMAGE: fields.CharField, 16 | FieldType.DATE: fields.DateField, 17 | FieldType.TIME: fields.TimeField, 18 | FieldType.DATETIME: fields.DateTimeField, 19 | FieldType.DURATION: fields.DurationField, 20 | } 21 | 22 | # 导出的字段和序列化字段的映射 23 | ExportFieldTypeSerializerMap = { 24 | FieldType.BOOL: drf_field.ExportBooleanField, 25 | FieldType.DATETIME: drf_field.ExportDateTimeField, 26 | } 27 | -------------------------------------------------------------------------------- /api_basebone/core/__init__.py: -------------------------------------------------------------------------------- 1 | def register_annotated_field( 2 | model, field_name, field_type, annotation, display_name=None 3 | ): 4 | if display_name is None: 5 | display_name = field_name 6 | 7 | if 'GMeta' not in model.__dict__: 8 | if hasattr(model, 'GMeta'): 9 | class GMeta(model.GMeta): 10 | annotated_fields = {} 11 | else: 12 | class GMeta: 13 | pass 14 | model.GMeta = GMeta 15 | 16 | if not hasattr(model.GMeta, 'annotated_fields'): 17 | model.GMeta.annotated_fields = {} 18 | 19 | model.GMeta.annotated_fields[field_name] = { 20 | 'display_name': display_name, 21 | 'type': field_type, 22 | 'annotation': annotation, 23 | } 24 | 25 | 26 | def register_computed_field(model, field_name, field_type, prop, display_name=None, deps=None): 27 | setattr(model, field_name, property(prop)) 28 | 29 | if display_name is None: 30 | display_name = field_name 31 | 32 | if 'GMeta' not in model.__dict__: 33 | class GMeta: 34 | pass 35 | model.GMeta = GMeta 36 | 37 | if not hasattr(model.GMeta, 'computed_fields'): 38 | model.GMeta.computed_fields = [] 39 | cfg = { 40 | 'name': field_name, 41 | 'display_name': display_name, 42 | 'type': field_type, 43 | } 44 | 45 | if deps is not None: 46 | cfg['deps'] = deps 47 | 48 | model.GMeta.computed_fields.append(cfg) 49 | -------------------------------------------------------------------------------- /api_basebone/core/const.py: -------------------------------------------------------------------------------- 1 | """ 2 | 各种常量说明 3 | """ 4 | 5 | """客户端传入过滤条件的关键字 6 | 7 | 客户端可通过此进行数据过滤和筛选 8 | 9 | 支持的方法:POST 当前适用于列表方法 10 | 数据格式 List: 11 | 12 | [ 13 | { 14 | 'field': 字段名, 15 | 'operator': 运算符, 16 | 'value': 值, 17 | }, 18 | ... 19 | ] 20 | """ 21 | FILTER_CONDITIONS = 'filters' 22 | 23 | """客户端传入排除条件的关键字 24 | 25 | 客户端可通过此进行数据排除 26 | 27 | 支持的方法:POST 当前适用于列表方法 28 | 数据格式 List: 29 | 30 | [ 31 | { 32 | 'field': 字段名, 33 | 'operator': 运算符, 34 | 'value': 值, 35 | }, 36 | ... 37 | ] 38 | """ 39 | EXCLUDE_CONDITIONS = 'excludes' 40 | 41 | """客户端传入展示字段的关键字 42 | """ 43 | DISPLAY_FIELDS = 'display_fields' 44 | 45 | """展开字段的传入 46 | 47 | 支持的方法:POST 当前适用于列表方法 48 | 49 | 数据格式 List:[字段名, 字段名.字段名, 字段名, ...] 50 | """ 51 | EXPAND_FIELDS = 'expand_fields' 52 | 53 | """排序字段 54 | 55 | 支持的方法:POST 当前适用于列表方法 56 | 57 | 数据格式 List:[字段名, 字段名, ....] 58 | """ 59 | ORDER_BY_FIELDS = 'order_by' 60 | 61 | """客户端请求的数据结构为树形 62 | 63 | 支持的方法:POST, GET 当前适用于列表方法 64 | 65 | 数据格式 Bool:true 或者 false 66 | """ 67 | DATA_WITH_TREE = 'data_with_tree' 68 | 69 | """在序列化时,指定排除的字段,数据格式为列表或者元组""" 70 | GMETA_SERIALIZER_EXCLUDE_FIELDS = 'exclude_fields' 71 | 72 | """输出 schema 中的 title_field,数据类型:字符串""" 73 | GMETA_TITLE_FIELD = 'title_field' 74 | 75 | """ 76 | 字段的重置配置,例如觉得字段默认的 verbose_name 不好看可以使用此配置,例子如下: 77 | 78 | field_form_config = { 79 | 'comments': { 80 | 'verbose_name': '我是谁', 81 | 'required': False, 82 | } 83 | } 84 | """ 85 | GMETA_FIELD_CONFIG = 'field_form_config' 86 | 87 | """django 中声明的字段的配置和输出配置键的映射 88 | 89 | 此常量供业务内部调用 90 | """ 91 | GMETA_FIELD_CONFIG_MAP = {'verbose_name': 'displayName'} 92 | 93 | """ 94 | api接口中,条件更新的接口update_by_condition中采用,表示update语句set的列值对 95 | """ 96 | SET_FIELDS = 'set_fields' 97 | -------------------------------------------------------------------------------- /api_basebone/core/object_model.py: -------------------------------------------------------------------------------- 1 | from django.db.models.fields import Field 2 | 3 | 4 | class Options: 5 | """ 6 | 为了满足api_basebone.export.fields.get_model_field_config 7 | 可能需要添加其他方法 8 | """ 9 | def __init__(self, fields, model): 10 | self.fields = fields 11 | self.model = model 12 | self.attach_field_attr() 13 | 14 | def attach_field_attr(self): 15 | for field in self.fields: 16 | field.concrete = True 17 | field.model = self.model 18 | 19 | def get_fields(self): 20 | return self.fields 21 | 22 | 23 | class ModelBase(type): 24 | """Metaclass for JsonObjectModel models.""" 25 | 26 | def __new__(cls, name, bases, attrs): 27 | new_attrs = {} 28 | fields = [] 29 | for key, value in attrs.items(): 30 | if isinstance(value, Field): 31 | value.name = key 32 | fields.append(value) 33 | _meta = Options(fields, cls) 34 | _meta.model_name = name.lower() 35 | _meta.verbose_name = getattr(attrs.pop('Meta'), 'verbose_name', _meta.model_name) if 'Meta' in attrs else _meta.model_name 36 | _meta.app_label = '' 37 | new_attrs['_meta'] = _meta 38 | new_attrs['__module__'] = attrs['__module__'] 39 | model = super(ModelBase, cls).__new__(cls, name, bases, new_attrs) 40 | return model 41 | 42 | 43 | class ObjectModel(metaclass=ModelBase): 44 | """ 45 | 用来描述json对象结构的模型 46 | 为了满足api_basebone.export.fields.get_model_field_config 47 | """ 48 | 49 | class Meta: 50 | abstract = True 51 | -------------------------------------------------------------------------------- /api_basebone/core/widgets.py: -------------------------------------------------------------------------------- 1 | from enum import Enum, unique 2 | 3 | 4 | @unique 5 | class BSMWidgets(Enum): 6 | """存放各种组件常量 7 | 8 | 保证和前端配置一致,同时是为了避免手写错误 9 | """ 10 | 11 | TextInput = 'TextInput' 12 | TextArea = 'TextArea' 13 | RichTextEditor = 'RichTextEditor' 14 | NumberInput = 'NumberInput' 15 | Switch = 'Switch' 16 | Select = 'Select' 17 | InlineForm = 'InlineForm' 18 | InnerTable = 'InnerTable' 19 | TimePicker = 'TimePicker' 20 | ImageUploader = 'ImageUploader' 21 | FileUploader = 'FileUploader' 22 | Gallery = 'Gallery' 23 | Transfer = 'Transfer' 24 | TreeSelect = 'TreeSelect' 25 | PasswordInput = 'PasswordInput' 26 | Checkbox = 'Checkbox' 27 | Cascader = 'Cascader' 28 | Radio = 'Radio' 29 | SkuSpec = 'SkuSpec' # TODO 这个以及后面的控件,都是自定义控件,可以不写在这里。 30 | DistrictSelect = 'DistrictSelect' 31 | 32 | class Widgets: 33 | 34 | def __getattr__(self, key): 35 | widget = getattr(BSMWidgets, key, None) 36 | if not widget: 37 | raise AttributeError 38 | return widget.name 39 | 40 | 41 | widgets = Widgets() 42 | -------------------------------------------------------------------------------- /api_basebone/db/__init__.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import types 3 | 4 | from django.apps import apps 5 | 6 | from .query import QuerySetExtend 7 | 8 | 9 | def extend_objects(queryset_class): 10 | """ 11 | 迭代扩展查询方法 12 | """ 13 | 14 | for name, method in inspect.getmembers(queryset_class, predicate=inspect.isfunction): 15 | for model in apps.get_models(): 16 | setattr(model.objects, name, types.MethodType(method, model.objects)) 17 | 18 | 19 | extend_objects(QuerySetExtend) 20 | -------------------------------------------------------------------------------- /api_basebone/db/query.py: -------------------------------------------------------------------------------- 1 | from django.db import connections 2 | 3 | # from django.db.models import Model 4 | 5 | 6 | class QuerySetExtend: 7 | """查询扩展""" 8 | 9 | def count(self): 10 | 11 | """Counting all rows is very expensive on large Innodb tables. This 12 | is a replacement for QuerySet that returns an approximation if count() 13 | is called with no additional constraints. In all other cases it should 14 | behave exactly as QuerySet. 15 | 16 | Only works with MySQL. Behaves normally for all other engines. 17 | """ 18 | 19 | # Code from django/db/models/query.py 20 | 21 | queryset_instance = self.get_queryset() 22 | 23 | if queryset_instance._result_cache is not None: 24 | return len(queryset_instance._result_cache) 25 | 26 | is_mysql = 'mysql' in connections[self.db].client.executable_name.lower() 27 | 28 | query = queryset_instance.query 29 | 30 | if ( 31 | is_mysql 32 | and not query.where 33 | and query.high_mark is None 34 | and query.low_mark == 0 35 | and not query.select 36 | and not query.group_by 37 | and not query.having 38 | and not query.distinct 39 | ): 40 | # If query has no constraints, we would be simply doing 41 | # "SELECT COUNT(*) FROM foo". Monkey patch so the we 42 | # get an approximation instead. 43 | cursor = connections[self.db].cursor() 44 | cursor.execute("SHOW TABLE STATUS LIKE %s", (self.model._meta.db_table,)) 45 | return cursor.fetchall()[0][4] 46 | else: 47 | return query.get_count(using=self.db) 48 | -------------------------------------------------------------------------------- /api_basebone/drf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/drf/__init__.py -------------------------------------------------------------------------------- /api_basebone/drf/authentication.py: -------------------------------------------------------------------------------- 1 | import json 2 | import hashlib 3 | 4 | from rest_framework.exceptions import PermissionDenied 5 | from rest_framework.authentication import SessionAuthentication 6 | 7 | 8 | class CsrfExemptSessionAuthentication(SessionAuthentication): 9 | def enforce_csrf(self, request): 10 | pass 11 | 12 | 13 | class SignatureSessionAuthentication(SessionAuthentication): 14 | def authenticate(self, request): 15 | if request.data: 16 | try: 17 | body_str = json.dumps(request.data, ensure_ascii=False, separators=(',', ':')) 18 | except: 19 | pass # multipart/form-data请求不能序列化,先跳过 20 | else: 21 | sign = hashlib.sha256(body_str.encode()).hexdigest() 22 | if sign != request.META['HTTP_X_SIGNATURE']: 23 | raise PermissionDenied() 24 | return super().authenticate(request) 25 | -------------------------------------------------------------------------------- /api_basebone/drf/pagination.py: -------------------------------------------------------------------------------- 1 | from rest_framework.pagination import ( 2 | _positive_int, 3 | PageNumberPagination as OriginPageNumberPagination, 4 | ) 5 | from rest_framework.response import Response 6 | 7 | 8 | class PageNumberPagination(OriginPageNumberPagination): 9 | 10 | max_page_size = 1000 11 | page_size = 100 12 | page_query_param = 'page' 13 | page_size_query_param = 'size' 14 | 15 | def get_page_size(self, request): 16 | """重写此方法是为了支持以下场景 17 | 18 | - 当传入的数据包含分页参数时,返回对应的分页数据结构 19 | - 当传入的数据不包含分页参数时,直接返回非分页数据的数据结构 20 | """ 21 | if self.page_size_query_param: 22 | try: 23 | return _positive_int( 24 | request.query_params[self.page_size_query_param], 25 | strict=True, 26 | cutoff=self.max_page_size, 27 | ) 28 | except (KeyError, ValueError): 29 | return 30 | return self.page_size 31 | -------------------------------------------------------------------------------- /api_basebone/drf/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import IsAdminUser as OriginIsAdminUser 2 | from django.conf import settings 3 | 4 | 5 | from api_basebone.utils.sign import common_make_sign 6 | 7 | 8 | class IsAdminUser(OriginIsAdminUser): 9 | """ 10 | Allows access only to admin users. 11 | 12 | 这里使用了签名认证和原有的会话认证 13 | 14 | 如果请求的头部包含了签名的参数,则使用签名校验,否则使用 15 | 原有的会话认证 16 | """ 17 | 18 | def check_with_sign(self, request): 19 | meta = request.META 20 | sign_key_list = [ 21 | 'HTTP_X_API_TIMESTAMP', 22 | 'HTTP_X_API_NONCESTR', 23 | 'HTTP_X_API_SIGNATURE', 24 | ] 25 | return all([key in meta for key in sign_key_list]) 26 | 27 | def validate_sign(self, request): 28 | """校验签名是否正常""" 29 | key = settings.BUSINESS_KEY 30 | secret = settings.BUSINESS_SECRET 31 | timestamp = request.META.get('HTTP_X_API_TIMESTAMP') 32 | noncestr = request.META.get('HTTP_X_API_NONCESTR') 33 | sign = request.META.get('HTTP_X_API_SIGNATURE') 34 | return sign == common_make_sign(key, secret, timestamp, noncestr) 35 | 36 | def has_permission(self, request, view): 37 | if not self.check_with_sign(request): 38 | return request.user and request.user.is_staff 39 | else: 40 | return self.validate_sign(request) 41 | -------------------------------------------------------------------------------- /api_basebone/export/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/export/__init__.py -------------------------------------------------------------------------------- /api_basebone/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/management/__init__.py -------------------------------------------------------------------------------- /api_basebone/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/management/commands/__init__.py -------------------------------------------------------------------------------- /api_basebone/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-05-17 08:11 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import jsonfield.fields 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='AdminLog', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('action_time', models.DateTimeField(auto_now_add=True, verbose_name='发生时间')), 23 | ('action', models.CharField(blank=True, default='', max_length=20, verbose_name='动作')), 24 | ('app_label', models.CharField(blank=True, default='', max_length=20, verbose_name='应用标识')), 25 | ('model_slug', models.CharField(blank=True, default='', max_length=30, verbose_name='模型标识')), 26 | ('object_id', models.CharField(blank=True, default='', max_length=20, verbose_name='数据ID')), 27 | ('message', models.CharField(blank=True, default='', max_length=50, verbose_name='消息')), 28 | ('params', jsonfield.fields.JSONField(default={})), 29 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), 30 | ], 31 | options={ 32 | 'verbose_name': '动作日志记录', 33 | 'verbose_name_plural': '动作日志记录', 34 | }, 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /api_basebone/migrations/0002_auto_20190524_1155.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-05-24 03:55 2 | 3 | import api_basebone.core.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('api_basebone', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='adminlog', 16 | name='params', 17 | field=api_basebone.core.fields.JSONField(default={}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /api_basebone/migrations/0004_auto_20190910_2242.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-09-10 14:42 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0003_api_field_filter_parameter'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='filter', 15 | name='type', 16 | field=models.CharField(choices=[('CONTAINER', '容器'), ('CHILD', '单一条件')], max_length=50, verbose_name='条件类型'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /api_basebone/migrations/0005_auto_20190910_2244.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-09-10 14:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0004_auto_20190910_2242'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='filter', 15 | name='type', 16 | field=models.IntegerField(choices=[(0, '容器'), (1, '单一条件')], verbose_name='条件类型'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /api_basebone/migrations/0006_auto_20190910_2246.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-09-10 14:46 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('api_basebone', '0005_auto_20190910_2244'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='filter', 16 | name='parenet', 17 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api_basebone.Filter', verbose_name='parenet'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /api_basebone/migrations/0007_auto_20190910_2247.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-09-10 14:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0006_auto_20190910_2246'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='filter', 15 | name='name', 16 | field=models.CharField(max_length=50, null=True, verbose_name='条件字段名'), 17 | ), 18 | migrations.AlterField( 19 | model_name='filter', 20 | name='value', 21 | field=models.CharField(max_length=100, null=True, verbose_name='条件值'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /api_basebone/migrations/0008_auto_20190910_2254.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-09-10 14:54 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0007_auto_20190910_2247'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='filter', 15 | old_name='name', 16 | new_name='field', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /api_basebone/migrations/0009_auto_20190911_1547.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-09-11 07:47 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('api_basebone', '0008_auto_20190910_2254'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='field', 16 | options={'ordering': ['name'], 'verbose_name': 'API的字段', 'verbose_name_plural': 'API的字段'}, 17 | ), 18 | migrations.RemoveField( 19 | model_name='filter', 20 | name='parenet', 21 | ), 22 | migrations.AddField( 23 | model_name='filter', 24 | name='parent', 25 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api_basebone.Filter', verbose_name='parent'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /api_basebone/migrations/0010_filter_layer.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-09-11 08:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0009_auto_20190911_1547'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='filter', 15 | name='layer', 16 | field=models.IntegerField(default=0, verbose_name='嵌套层数'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /api_basebone/migrations/0012_auto_20190930_1715.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-09-30 09:15 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0011_auto_20190924_1142'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='api', 15 | name='demo', 16 | field=models.TextField(default='', verbose_name='api返回格式范例'), 17 | ), 18 | migrations.AddField( 19 | model_name='api', 20 | name='summary', 21 | field=models.TextField(default='', verbose_name='api说明'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /api_basebone/migrations/0013_auto_20191012_1642.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-10-12 08:42 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0012_auto_20190930_1715'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='api', 15 | name='expand_fields', 16 | field=models.CharField(blank=True, default='', max_length=500, verbose_name='展开字段'), 17 | ), 18 | migrations.AlterField( 19 | model_name='api', 20 | name='ordering', 21 | field=models.CharField(blank=True, default='', max_length=500, verbose_name='排序'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /api_basebone/migrations/0014_auto_20191014_1518.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-10-14 07:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0013_auto_20191012_1642'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='api', 15 | name='config', 16 | field=models.TextField(default='', verbose_name='配置json数据'), 17 | ), 18 | migrations.AlterField( 19 | model_name='parameter', 20 | name='type', 21 | field=models.CharField(choices=[('string', '字符串'), ('int', '整数'), ('decimal', '浮点数'), ('boolean', '布尔值'), ('json', 'json格式'), ('PAGE_SIZE', '页长'), ('PAGE_IDX', '页码'), ('pk', '主键')], max_length=20, verbose_name='参数类型'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /api_basebone/migrations/0015_parameter_is_array.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-10-18 08:55 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0014_auto_20191014_1518'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='parameter', 15 | name='is_array', 16 | field=models.BooleanField(default=False, verbose_name='是否数组'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /api_basebone/migrations/0016_auto_20191022_1928.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-10-22 11:28 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('api_basebone', '0015_parameter_is_array'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='parameter', 16 | name='layer', 17 | field=models.IntegerField(default=0, verbose_name='嵌套层数'), 18 | ), 19 | migrations.AddField( 20 | model_name='parameter', 21 | name='parent', 22 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='api_basebone.Parameter', verbose_name='parent'), 23 | ), 24 | migrations.AlterField( 25 | model_name='parameter', 26 | name='type', 27 | field=models.CharField(choices=[('string', '字符串'), ('int', '整数'), ('decimal', '浮点数'), ('boolean', '布尔值'), ('json', 'json格式'), ('object', '复杂类型'), ('PAGE_SIZE', '页长'), ('PAGE_IDX', '页码'), ('pk', '主键')], max_length=20, verbose_name='参数类型'), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /api_basebone/migrations/0017_auto_20191101_1829.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-11-01 10:29 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0016_auto_20191022_1928'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='displayfield', 15 | name='api', 16 | ), 17 | migrations.RemoveField( 18 | model_name='filter', 19 | name='api', 20 | ), 21 | migrations.RemoveField( 22 | model_name='filter', 23 | name='parent', 24 | ), 25 | migrations.RemoveField( 26 | model_name='parameter', 27 | name='api', 28 | ), 29 | migrations.RemoveField( 30 | model_name='parameter', 31 | name='parent', 32 | ), 33 | migrations.RemoveField( 34 | model_name='setfield', 35 | name='api', 36 | ), 37 | migrations.DeleteModel( 38 | name='Api', 39 | ), 40 | migrations.DeleteModel( 41 | name='DisplayField', 42 | ), 43 | migrations.DeleteModel( 44 | name='Filter', 45 | ), 46 | migrations.DeleteModel( 47 | name='Parameter', 48 | ), 49 | migrations.DeleteModel( 50 | name='SetField', 51 | ), 52 | ] 53 | -------------------------------------------------------------------------------- /api_basebone/migrations/0018_auto_20201225_1704.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-12-25 17:04 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api_basebone', '0017_auto_20191101_1829'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='adminlog', 15 | name='message', 16 | field=models.TextField(blank=True, default='', verbose_name='消息'), 17 | ), 18 | migrations.AlterField( 19 | model_name='adminlog', 20 | name='object_id', 21 | field=models.TextField(blank=True, default='', verbose_name='数据ID'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /api_basebone/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/migrations/__init__.py -------------------------------------------------------------------------------- /api_basebone/permissions.py: -------------------------------------------------------------------------------- 1 | 2 | class BasePermission(object): 3 | 4 | def has_permission(self, user, model, func_name, params, request): 5 | raise NotImplementedError() -------------------------------------------------------------------------------- /api_basebone/readme.md: -------------------------------------------------------------------------------- 1 | ### 1 2020-06-19 管理端接口添加权限 2 | 3 | 添加人:Allen 4 | 5 | 业务描述:管理端细致到用户对每个接口有没有对应的权限进行操作 6 | 7 | 在 settings.py 文件中添加对应的配置进行设置 8 | 9 | ```python 10 | BASEBONE_API_SERVICE = { 11 | ... # 管理端是否开启接口权限校验 12 | MANAGE_API_PERMISSION_VALIDATE_ENABLE = True | False 13 | ... 14 | } 15 | ``` 16 | 17 | 使用此开关是否启用接口权限校验 18 | 19 | 每个接口权限是根据 20 | 21 | ```python 22 | 23 | 通用的命名 24 | 25 | basebone_api_model_{接口方法名称 | 云函数名称} 格式进行命名 26 | 27 | 如果是云函数 28 | 29 | basebone_api_model_func_{云函数名称} 格式进行命名 30 | ``` 31 | -------------------------------------------------------------------------------- /api_basebone/restful/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/restful/__init__.py -------------------------------------------------------------------------------- /api_basebone/restful/client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/restful/client/__init__.py -------------------------------------------------------------------------------- /api_basebone/restful/client/urls.py: -------------------------------------------------------------------------------- 1 | from api_basebone.drf.routers import BaseBoneSimpleRouter as SimpleRouter 2 | from api_basebone.restful.client.views import CommonManageViewSet as ClientViewSet 3 | 4 | router = SimpleRouter(custom_base_name='common-client') 5 | router.register('', ClientViewSet) 6 | 7 | urlpatterns = router.urls 8 | -------------------------------------------------------------------------------- /api_basebone/restful/const.py: -------------------------------------------------------------------------------- 1 | MANAGE_END_SLUG = 'manage' 2 | CLIENT_END_SLUG = 'client' 3 | -------------------------------------------------------------------------------- /api_basebone/restful/export/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/restful/export/__init__.py -------------------------------------------------------------------------------- /api_basebone/restful/export/formatter.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | Param = namedtuple('Param', ['name', 'display_name', 'type', 'required', 'default', 'choices']) 4 | Param.__new__.__defaults__ = (None,) * len(Param._fields) 5 | _FORMATTERS = {} 6 | 7 | def formatter(name=None, params=[]): 8 | def deco_func(func): 9 | # 做注册工作 10 | func_name = name if name else func.__name__ 11 | _FORMATTERS[func_name] = (params, func) 12 | return func 13 | return deco_func 14 | 15 | def format(value, formatter, params=None): 16 | if formatter not in _FORMATTERS: 17 | return value 18 | func = _FORMATTERS[formatter][1] 19 | # TODO,校验一下参数合法性 20 | return func(value, **params) 21 | 22 | @formatter(name='prefix', params=[ 23 | Param(name="prefix", display_name='前缀', type='str', required=True) 24 | ]) 25 | def prefix_formatter(value, prefix): 26 | return f'{prefix}{str(value)}' 27 | 28 | @formatter(name='suffix', params=[ 29 | Param(name="suffix", display_name='后缀', type='str', required=True) 30 | ]) 31 | def suffix_formatter(value, suffix): 32 | return f'{str(value)}{suffix}' 33 | 34 | @formatter(name='bothfix', params=[ 35 | Param(name="prefix", display_name='前缀', type='str'), 36 | Param(name="suffix", display_name='后缀', type='str') 37 | ]) 38 | def bothfix_formatter(value, prefix='', suffix=''): 39 | result = value 40 | if prefix: 41 | result = prefix_formatter(result, prefix) 42 | if suffix: 43 | result = suffix_formatter(result, suffix) 44 | return result 45 | -------------------------------------------------------------------------------- /api_basebone/restful/manage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/restful/manage/__init__.py -------------------------------------------------------------------------------- /api_basebone/restful/manage/config_urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | 输出配置的路由 3 | """ 4 | 5 | from api_basebone.drf.routers import BaseBoneSimpleRouter as SimpleRouter 6 | from api_basebone.restful.manage.config_views import ConfigViewSet 7 | 8 | router = SimpleRouter(custom_base_name='schema-config') 9 | router.register('', ConfigViewSet) 10 | 11 | urlpatterns = router.urls 12 | -------------------------------------------------------------------------------- /api_basebone/restful/manage/urls.py: -------------------------------------------------------------------------------- 1 | from api_basebone.drf.routers import BaseBoneSimpleRouter as SimpleRouter 2 | from api_basebone.restful.manage.views import CommonManageViewSet 3 | 4 | router = SimpleRouter(custom_base_name='common-manage') 5 | router.register('', CommonManageViewSet) 6 | 7 | urlpatterns = router.urls 8 | -------------------------------------------------------------------------------- /api_basebone/restful/relation/__init__.py: -------------------------------------------------------------------------------- 1 | from .reverse_m2m import reverse_many_to_many 2 | 3 | __all__ = ['reverse_many_to_many'] 4 | -------------------------------------------------------------------------------- /api_basebone/restful/viewsets.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from rest_framework.decorators import action 3 | 4 | from api_basebone.core import const 5 | from api_basebone.services import rest_services 6 | 7 | 8 | class BSMModelViewSet(viewsets.ModelViewSet): 9 | def perform_create(self, serializer): 10 | return serializer.save() 11 | 12 | def perform_update(self, serializer): 13 | return serializer.save() 14 | 15 | def list(self, request, *args, **kwargs): 16 | """获取列表数据""" 17 | display_fields = request.data.get(const.DISPLAY_FIELDS) 18 | return rest_services.display(self, display_fields) 19 | 20 | @action(methods=['POST'], detail=False, url_path='list') 21 | def set(self, request, app, model, **kwargs): 22 | """获取列表数据""" 23 | return self.list(request, app, model, **kwargs) 24 | 25 | def retrieve(self, request, *args, **kwargs): 26 | """获取数据详情""" 27 | display_fields = request.data.get(const.DISPLAY_FIELDS) 28 | return rest_services.retrieve(self, display_fields) 29 | 30 | def destroy(self, request, *args, **kwargs): 31 | """删除数据""" 32 | return rest_services.destroy(self, request) 33 | -------------------------------------------------------------------------------- /api_basebone/sandbox/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/sandbox/__init__.py -------------------------------------------------------------------------------- /api_basebone/sandbox/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | class LogCollector(object): 4 | 5 | def __init__(self, name, logger_type="function"): 6 | self.messages = [] 7 | self.name = name 8 | self.logger_type = logger_type 9 | 10 | @property 11 | def logger(self): 12 | if not getattr(self, '_logger', None): 13 | prefix = { 14 | 'function': 'lightning_cloud_function', 15 | 'trigger': 'lightning_trigger_script' 16 | }[self.logger_type] 17 | self._logger = logging.getLogger(f'{prefix}.{self.name}') 18 | return self._logger 19 | 20 | def log(self, level, message): 21 | level_func = { 22 | logging.DEBUG: self.logger.debug, 23 | logging.INFO: self.logger.info, 24 | logging.WARN: self.logger.warn, 25 | logging.ERROR: self.logger.error, 26 | logging.CRITICAL: self.logger.critical 27 | } 28 | self.messages.append((level, f'【Server-{self.logger_type}】{self.name}: {message}')) 29 | level_func[level](f'{self.name} {message}') 30 | 31 | def info(self, message): 32 | self.log(logging.INFO, message) 33 | 34 | def error(self, message): 35 | self.log(logging.ERROR, message) 36 | 37 | def warn(self, message): 38 | self.log(logging.WARN, message) 39 | 40 | def debug(self, message): 41 | self.log(logging.DEBUG, message) 42 | 43 | def critical(self, message): 44 | self.log(logging.CRITICAL, message) 45 | 46 | def collect(self): 47 | return self.messages -------------------------------------------------------------------------------- /api_basebone/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/services/__init__.py -------------------------------------------------------------------------------- /api_basebone/services/functions.py: -------------------------------------------------------------------------------- 1 | from django.db.models import Aggregate, CharField 2 | 3 | class GroupConcat(Aggregate): 4 | function = 'GROUP_CONCAT' 5 | template = '%(function)s(%(distinct)s%(expressions)s)' 6 | 7 | def __init__(self, expression, distinct=False, **extra): 8 | super(GroupConcat, self).__init__( 9 | expression, 10 | distinct='DISTINCT ' if distinct else '', 11 | output_field=CharField(), 12 | **extra) 13 | -------------------------------------------------------------------------------- /api_basebone/settings.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings as django_settings 2 | 3 | DEFAULTS = { 4 | # 管理端是否使用日志记录器 5 | 'MANAGE_USE_ACTION_LOG': True, 6 | 'MANAGE_API_PERMISSION_VALIDATE_ENABLE': False, 7 | # 管理端操作数据时权限检测 8 | 'MANAGE_GUARDIAN_DATA_PERMISSION_CHECK': False, 9 | # 管理端使用 guardian 检测的应用模型,元素数据格式为 {app_name}__{model_name} 10 | 'MANAGE_GUARDIAN_DATA_APP_MODELS': [], 11 | } 12 | 13 | 14 | class BaseBoneSettings(object): 15 | def __init__(self, user_settings=None, defaults=None): 16 | if user_settings: 17 | self._user_settings = self.__check_user_settings(user_settings) 18 | self.defaults = defaults or DEFAULTS 19 | self._cached_attrs = set() 20 | 21 | @property 22 | def user_settings(self): 23 | if not hasattr(self, '_user_settings'): 24 | self._user_settings = getattr(django_settings, 'BASEBONE_API_SERVICE', {}) 25 | return self._user_settings 26 | 27 | def __getattr__(self, attr): 28 | if attr not in self.defaults: 29 | raise AttributeError("Invalid API setting: '%s'" % attr) 30 | 31 | try: 32 | value = self.user_settings[attr] 33 | except KeyError: 34 | value = self.defaults[attr] 35 | 36 | self._cached_attrs.add(attr) 37 | setattr(self, attr, value) 38 | return value 39 | 40 | def __check_user_settings(self, user_settings): 41 | return user_settings 42 | 43 | 44 | settings = BaseBoneSettings(None, DEFAULTS) 45 | -------------------------------------------------------------------------------- /api_basebone/signals/__init__.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import Signal 2 | 3 | post_bsm_create = Signal(providing_args=['instance', 'create', 'request', 'old_instance']) 4 | before_bsm_create = Signal(providing_args=['instance', 'create', 'request']) 5 | post_bsm_delete = Signal(providing_args=['instance', 'request']) 6 | before_bsm_delete = Signal(providing_args=['instance', 'request']) 7 | -------------------------------------------------------------------------------- /api_basebone/tasks.py: -------------------------------------------------------------------------------- 1 | from celery import shared_task 2 | from django.core.mail import get_connection, send_mail 3 | from django.conf import settings 4 | 5 | from api_basebone.core.exceptions import BusinessException 6 | from api_basebone.utils.dingding import dingding_robot 7 | from bsm_config.settings import site_setting 8 | from api_basebone.utils.wechat import send_robot_text 9 | 10 | 11 | def task(func): 12 | if not hasattr(settings, 'CELERY_BROKER_URL'): 13 | return func 14 | return shared_task(func).delay 15 | 16 | 17 | @shared_task 18 | def dingding_robot_push(data, access_token_type='default'): 19 | """钉钉机器人发送消息""" 20 | dingding_robot.push_message(data, access_token_type=access_token_type) 21 | 22 | 23 | @task 24 | def send_email(subject, body, mail_to): 25 | if not site_setting['mail_protocol']: 26 | raise BusinessException(error_message='未配置邮件配置') 27 | with get_connection( 28 | host=site_setting['mail_host'], 29 | port=site_setting['mail_port'], 30 | username=site_setting['mail_need_login'] and site_setting['mail_username'], 31 | password=site_setting['mail_need_login'] and site_setting['mail_password'], 32 | use_tls=site_setting['start_tls'], 33 | use_ssl=site_setting['mail_protocol'] == 'SMTP_SSL', 34 | ) as connection: 35 | if type(mail_to) not in [tuple, list]: 36 | mail_to = [mail_to] 37 | print(send_mail(subject, body, f"{site_setting['sender_name']} <{site_setting['sender_address']}>", mail_to, connection=connection)) 38 | 39 | @shared_task 40 | def wechat_robot_push(app_id, message, mentioned_mobiles=[]): 41 | return send_robot_text(app_id, message, mentioned_mobiles) 42 | 43 | -------------------------------------------------------------------------------- /api_basebone/templates/bsm_admin_config.html: -------------------------------------------------------------------------------- 1 | {% load jsonify %}module.exports = {{ result | jsonify | safe }} 2 | -------------------------------------------------------------------------------- /api_basebone/templates/bsm_schema_config.html: -------------------------------------------------------------------------------- 1 | {% load jsonify %}{{ result | jsonify | safe }} 2 | -------------------------------------------------------------------------------- /api_basebone/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/templatetags/__init__.py -------------------------------------------------------------------------------- /api_basebone/templatetags/jsonify.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.template import Library 3 | 4 | register = Library() 5 | 6 | 7 | def jsonify(data): 8 | return json.dumps(data, ensure_ascii=False, indent=2) 9 | 10 | 11 | register.filter('jsonify', jsonify) 12 | -------------------------------------------------------------------------------- /api_basebone/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/tests/__init__.py -------------------------------------------------------------------------------- /api_basebone/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | 3 | app_name = 'basebone_common' 4 | 5 | urlpatterns = [ 6 | # 通用管理端 7 | path( 8 | 'basebone/__/', 9 | include( 10 | ('api_basebone.restful.manage.urls', app_name), 11 | namespace='manage.common.basebone', 12 | ), 13 | ), 14 | # 通用客户端 15 | path( 16 | 'basebone/client/__/', 17 | include( 18 | ('api_basebone.restful.client.urls', app_name), 19 | namespace='client.common.basebone', 20 | ), 21 | ), 22 | # 配置输出 23 | path( 24 | 'basebone/config/', 25 | include( 26 | ('api_basebone.restful.manage.config_urls', app_name), 27 | namespace='schema.config', 28 | ), 29 | ), 30 | # 通用 app 管理端 31 | path( 32 | 'basebone/manage/', 33 | include( 34 | ('api_basebone.app.manage_urls', app_name), namespace='manage.app.basebone' 35 | ), 36 | ), 37 | # 通用 app 管理端 38 | path( 39 | 'basebone/client/', 40 | include( 41 | ('api_basebone.app.client_urls', app_name), namespace='client.app.basebone' 42 | ), 43 | ), 44 | # 配置API接口处理器 45 | # path('api/', include(('api_basebone.api.urls', app_name), namespace='api')), 46 | # 配置API接口帮助文档配置 47 | # path( 48 | # 'api_doc/', include(('api_basebone.api.doc_urls', app_name), namespace='api_doc') 49 | # ), 50 | ] 51 | -------------------------------------------------------------------------------- /api_basebone/utils/__init__.py: -------------------------------------------------------------------------------- 1 | def get_lower_case_name(text): 2 | lst = [] 3 | for index, char in enumerate(text): 4 | if char.isupper() and index != 0: 5 | lst.append("_") 6 | lst.append(char) 7 | return "".join(lst).lower() -------------------------------------------------------------------------------- /api_basebone/utils/data.py: -------------------------------------------------------------------------------- 1 | from .meta import get_all_relation_fields 2 | 3 | 4 | def hand_signle_export_field_to_prefetch(model, field): 5 | """根据单个导出字段,筛选出 prefetch 的字段列表""" 6 | prefetch_list = [] 7 | inner_model = model 8 | 9 | field_split = field.split('.') 10 | for inner_item in field_split: 11 | relation_fields = [item.name for item in get_all_relation_fields(inner_model)] 12 | 13 | if inner_item in relation_fields: 14 | prefetch_list.append(inner_item) 15 | inner_model = inner_model._meta.get_field(inner_item).related_model 16 | 17 | if len(prefetch_list): 18 | return '.'.join(prefetch_list) 19 | 20 | 21 | def get_prefetch_fields_from_export_fields(model, fields=None): 22 | """ 23 | 从导出配置中获取 prefetch 字段,针对的是 v2 版本 24 | """ 25 | prefetch_set = set() 26 | 27 | for item in fields: 28 | field_str = item[0] if isinstance(item, list) or isinstance(item, tuple) else item 29 | prefetch_field = hand_signle_export_field_to_prefetch(model, field_str) 30 | 31 | if prefetch_field: 32 | prefetch_set.add(prefetch_field) 33 | return list(prefetch_set) 34 | -------------------------------------------------------------------------------- /api_basebone/utils/dingding.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | 4 | from django.conf import settings 5 | 6 | 7 | class DingDingRobot: 8 | """钉钉消息通知机器人""" 9 | 10 | BASE_URL = 'https://oapi.dingtalk.com/robot/send?access_token={}' 11 | 12 | def _get_url(self, access_token): 13 | return self.BASE_URL.format(access_token) 14 | 15 | def _push_notify(self, data, access_token_type='default'): 16 | """推送通知, 通知只会通知文本消息 17 | 18 | Params: 19 | access_token str 消息机器人的访问令牌 20 | data str | dict | tuple 数据 21 | """ 22 | access_token_map = getattr(settings, 'DINGDING_ROBOT_ACCESS_TOKEN_MAP', None) 23 | if not access_token_map or not isinstance(access_token_map, dict): 24 | return 25 | 26 | if access_token_type not in access_token_map: 27 | return 28 | 29 | access_token = access_token_map[access_token_type] 30 | if not access_token: 31 | return 32 | 33 | if not isinstance(data, str): 34 | try: 35 | data = str(data) 36 | except Exception: 37 | return 38 | 39 | content = { 40 | "msgtype": "text", 41 | "text": { 42 | "content": data 43 | } 44 | } 45 | 46 | url = self._get_url(access_token) 47 | 48 | try: 49 | requests.post(url, json=content) 50 | except Exception: 51 | pass 52 | 53 | def push_message(self, data, access_token_type='default'): 54 | """推送消息""" 55 | self._push_notify(data, access_token_type=access_token_type) 56 | 57 | 58 | dingding_robot = DingDingRobot() 59 | -------------------------------------------------------------------------------- /api_basebone/utils/format.py: -------------------------------------------------------------------------------- 1 | import arrow 2 | from django.conf import settings 3 | 4 | 5 | def camel_to_underline(camel_format): 6 | """驼峰命名格式转下划线命名格式""" 7 | 8 | underline_format = '' 9 | if isinstance(camel_format, str): 10 | for _s_ in camel_format: 11 | underline_format += _s_ if _s_.islower() else '_'+_s_.lower() 12 | return underline_format 13 | 14 | 15 | def first_lower(data): 16 | if data: 17 | return data[:1].lower() + data[1:] 18 | return '' 19 | 20 | 21 | def underline_to_camel(underline_format): 22 | """下划线命名格式驼峰命名格式 23 | """ 24 | camel_format = '' 25 | if isinstance(underline_format, str): 26 | for item in underline_format.split('_'): 27 | camel_format += item.capitalize() 28 | return first_lower(camel_format) 29 | 30 | 31 | def format_human_datetime(value): 32 | """格式化时间""" 33 | if not value: 34 | return '' 35 | 36 | return arrow.get(value).to(settings.TIME_ZONE).format( 37 | 'YYYY-MM-DD HH:mm:ss' 38 | ) 39 | -------------------------------------------------------------------------------- /api_basebone/utils/gmeta.py: -------------------------------------------------------------------------------- 1 | """ 2 | 这里是处理模型中 GMeta 中的业务逻辑 3 | """ 4 | 5 | from django.contrib.auth import get_user_model 6 | from api_basebone.core import gmeta as gmeta_const 7 | from .meta import get_concrete_fields 8 | 9 | 10 | def get_gmeta_class(model): 11 | """ 12 | 获取模型的 GMeta 类 13 | """ 14 | return getattr(model, 'GMeta', None) 15 | 16 | 17 | def get_gmeta_pure_config(model, config_key): 18 | """ 19 | 获取模型的 GMeta 类中指定键的配置 20 | 21 | 这个方法只是粗糙的获取配置,没有做任何的校验,所以这个方法获取 22 | 到的配置不一定是合法的 23 | """ 24 | gmeta_class = get_gmeta_class(model) 25 | if not gmeta_class: 26 | return 27 | 28 | return getattr(gmeta_class, config_key, None) 29 | 30 | 31 | def get_gmeta_client_user_field(model, config_key): 32 | """ 33 | 获取用户字段的配置 34 | """ 35 | user_field_name = get_gmeta_pure_config(model, config_key) 36 | if not user_field_name: 37 | return 38 | 39 | field_name_map = {item.name: item for item in get_concrete_fields(model)} 40 | user_filed = field_name_map.get(user_field_name) 41 | 42 | # 如果获取的字段为空或者获取到字段的关系模型不是 User,则返回 None 43 | if not user_filed or user_filed.related_model is not get_user_model(): 44 | return 45 | return user_field_name 46 | 47 | 48 | def get_gmeta_config_by_key(model, config_key): 49 | """ 50 | 获取模型的 GMeta 类中指定键的配置 51 | 52 | 这个方法获取到的配置,是经过校验过的配置,可以放心使用 53 | """ 54 | 55 | handler_map = {gmeta_const.GMETA_CLIENT_USER_FIELD: get_gmeta_client_user_field} 56 | handler = handler_map.get(config_key) 57 | if handler: 58 | return handler(model, config_key) 59 | return get_gmeta_pure_config(model, config_key) 60 | 61 | 62 | def get_attr_in_gmeta_class(model, config_name, default_value=None): 63 | """获取指定模型 GMeta 类中指定的属性 64 | 65 | Params: 66 | model class django 模型类 67 | config_name string GMeta 类中配置项的名称 68 | default_value 任何数据类型 默认数据 69 | """ 70 | 71 | gmeta_class = getattr(model, 'GMeta', None) 72 | if not gmeta_class: 73 | return default_value 74 | return getattr(gmeta_class, config_name, default_value) 75 | -------------------------------------------------------------------------------- /api_basebone/utils/module.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | from django.conf import settings 3 | 4 | # admin 类 5 | BSM_ADMIN = 'admin' 6 | 7 | # 管理后台的批量操作 8 | BSM_BATCH_ACTION = 'actions' 9 | 10 | # 管理后台的的自定义表单 11 | BSM_FORM = 'forms' 12 | 13 | # 管理后台导出的自定义序列化类 14 | BSM_EXPORT = 'exports' 15 | 16 | BSM_GLOBAL_MODULE = 'bsmconfig' 17 | 18 | # 全局统一配置的菜单模块文件名 19 | BSM_GLOBAL_MODULE_MENU = 'menu' 20 | # 全局统一配置的菜单模块中声明的管理端的菜单的键 21 | BSM_GLOBAL_MODULE_MENU_MANAGE = 'MANAGE_MENU' 22 | 23 | # 全局统一的配置的模型角色配置的文件名 24 | BSM_GLOBAL_MODULE_ROLES = 'roles' 25 | 26 | """ 27 | 全局统一的配置的权限模块中对象的键 28 | 29 | 数据结构 30 | 31 | ROLES = { 32 | member__user: {, 33 | 'filters': 过滤条件' 34 | }, 35 | ... 36 | } 37 | """ 38 | BSM_GLOBAL_ROLES = 'ROLES' 39 | 40 | # 角色中的配置,是否使用 bsm admin 中的 use_admin_filter_by_login_user 配置进行筛选 41 | BSM_GLOBAL_ROLE_USE_ADMIN_FILTER_BY_LOGIN_USER = 'use_admin_filter_by_login_user' 42 | 43 | BSM_GLOBAL_ROLE_QS_DISTINCT = 'distinct' 44 | 45 | 46 | def get_admin_module(app_full_name, slug=BSM_ADMIN): 47 | """获取管理后台指定的模块""" 48 | try: 49 | module_name = f'{app_full_name}.bsm.{slug}' 50 | module = importlib.util.find_spec(module_name) 51 | if module: 52 | return importlib.import_module(module_name) 53 | except ModuleNotFoundError: 54 | return 55 | 56 | 57 | def import_class_from_string(value): 58 | """ 59 | 尝试从一个字符串中加载一个类 60 | """ 61 | try: 62 | module_path, class_name = value.rsplit('.', 1) 63 | module = importlib.import_module(module_path) 64 | return getattr(module, class_name) 65 | except (ImportError, AttributeError): 66 | raise ImportError(f'不能加载 {value}') 67 | 68 | 69 | def get_bsm_global_module(config_module_name): 70 | """获取全局配置的模块""" 71 | try: 72 | module_name = getattr(settings, 'BSM_GLOBAL_MODULE', BSM_GLOBAL_MODULE) 73 | module_name = f'{module_name}.{config_module_name}' 74 | module = importlib.util.find_spec(module_name) 75 | if module: 76 | return importlib.import_module(module_name) 77 | except ModuleNotFoundError: 78 | return 79 | -------------------------------------------------------------------------------- /api_basebone/utils/redis.py: -------------------------------------------------------------------------------- 1 | from django_redis import get_redis_connection 2 | 3 | redis_client = get_redis_connection() -------------------------------------------------------------------------------- /api_basebone/utils/retry.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from functools import wraps 3 | from django.conf import settings 4 | 5 | FUNCTION_RETRY_COUNT_LIMIT = getattr( 6 | settings, 'FUNCTION_RETRY_COUNT_LIMIT', 1) 7 | 8 | 9 | def func_retry(max_limit=FUNCTION_RETRY_COUNT_LIMIT): 10 | """给函数加上重试机制""" 11 | 12 | if inspect.isfunction(max_limit): 13 | func = max_limit 14 | max_limit = FUNCTION_RETRY_COUNT_LIMIT 15 | 16 | @wraps(func) 17 | def wrapper(*args, count=1, **kwargs): 18 | try: 19 | return func() 20 | except Exception: 21 | if count > max_limit: 22 | # 记录日志 23 | print('end..', count, args, kwargs) 24 | else: 25 | count = count + 1 26 | wrapper(*args, count=count, **kwargs) 27 | return wrapper 28 | else: 29 | def middle(func): 30 | 31 | @wraps(func) 32 | def wrapper(*args, count=1, **kwargs): 33 | try: 34 | return func() 35 | except Exception: 36 | if count > max_limit: 37 | # 记录日志 38 | print('end..', count, args, kwargs) 39 | else: 40 | count = count + 1 41 | wrapper(*args, count=count, **kwargs) 42 | return wrapper 43 | return middle 44 | 45 | 46 | @func_retry(max_limit=4) 47 | def retry(): 48 | raise Exception('this si exxception') 49 | print('world') 50 | 51 | 52 | @func_retry 53 | def retrys(): 54 | raise Exception('this si exxception') 55 | print('world') 56 | -------------------------------------------------------------------------------- /api_basebone/utils/sentry.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from raven import Client 3 | 4 | 5 | class SentryClient(object): 6 | """ 7 | Raven client 8 | 9 | 用法: 10 | 11 | try: 12 | 1 / 0 13 | except ZeroDivisionError: 14 | client.captureException() 15 | """ 16 | 17 | def __init__(self, *args, **kwargs): 18 | self._raven_client = None 19 | 20 | @property 21 | def client(self): 22 | """构建 client""" 23 | try: 24 | if self._raven_client is None: 25 | self._raven_client = Client(settings.RAVEN_CONFIG.get('dsn')) 26 | return self._raven_client 27 | except AttributeError: 28 | return 29 | 30 | 31 | sentry_client = SentryClient().client 32 | -------------------------------------------------------------------------------- /api_basebone/utils/sign.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import time 4 | import hashlib 5 | 6 | from django.conf import settings 7 | 8 | from .uuid import uuid4_hex 9 | 10 | 11 | logger = logging.getLogger('django') 12 | 13 | 14 | def common_make_sign(api_key=None, secret=None, timestamp=None, noncestr=None): 15 | """通用产生签名""" 16 | string_sign_temp = 'key={}&secret={}×tamp={}&nonce_str={}'.format( 17 | api_key, secret, timestamp, noncestr 18 | ) 19 | return hashlib.md5(string_sign_temp.encode('utf-8')).hexdigest().upper() 20 | 21 | 22 | def build_common_header(): 23 | """接收业务方数据通信的请求头设置""" 24 | 25 | timestamp = str(int(time.time())) 26 | noncestr = uuid4_hex() 27 | key = settings.BUSINESS_KEY 28 | secret = settings.BUSINESS_SECRET 29 | sign_str = common_make_sign(key, secret, timestamp, noncestr) 30 | 31 | headers = { 32 | 'Content-Type': 'application/json', 33 | 'X_API_SIGNATURE': sign_str, 34 | 'X_API_KEY': key, 35 | 'X_API_TIMESTAMP': timestamp, 36 | 'X_API_NONCESTR': noncestr, 37 | } 38 | return headers 39 | -------------------------------------------------------------------------------- /api_basebone/utils/timezone.py: -------------------------------------------------------------------------------- 1 | import arrow 2 | 3 | from django.conf import settings 4 | 5 | 6 | def local_timestamp(): 7 | return arrow.utcnow().to(settings.TIME_ZONE).timestamp 8 | 9 | 10 | def timestamp_serializer(timestamp): 11 | return arrow.get(timestamp).to(settings.TIME_ZONE).format('YYYY-MM-DD HH:mm:ss') 12 | -------------------------------------------------------------------------------- /api_basebone/utils/upload.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from minio import Minio 4 | from minio.datatypes import PostPolicy 5 | 6 | from api_basebone.utils.uuid import uuid4_hex 7 | from bsm_config.settings import site_setting 8 | 9 | 10 | # @lru_cache() 11 | def get_s3_client(): 12 | return Minio( 13 | '{}:{}'.format(site_setting['S3_ENDPOINT'], site_setting['S3_PORT']), 14 | site_setting['S3_ACCESS_KEY'], 15 | site_setting['S3_SECRET_KEY'], 16 | secure=site_setting['S3_USE_SSL'], 17 | ) 18 | 19 | 20 | def upload_file(*args, **kwargs): 21 | """ 22 | 使用方式:upload_file('存储路径', '本地文件路径') 23 | """ 24 | client = get_s3_client() 25 | client.fput_object( 26 | site_setting['S3_BUCKET'], 27 | *args, 28 | **kwargs, 29 | ) 30 | 31 | 32 | def s3_presign_url(filename): 33 | client = get_s3_client() 34 | return client.presigned_put_object(site_setting['S3_BUCKET'], filename) 35 | 36 | 37 | def s3_endpoint(): 38 | return '{}://{}:{}/{}/'.format( 39 | 'https' if site_setting['S3_USE_SSL'] else 'http', 40 | site_setting['S3_ENDPOINT'], 41 | site_setting['S3_PORT'], 42 | site_setting['S3_BUCKET'], 43 | ) 44 | 45 | 46 | def get_token(): 47 | # 现在每次上传之前都先更新一遍token,30秒足矣 48 | ttl = datetime.timedelta(seconds=30) 49 | policy = PostPolicy(site_setting['S3_BUCKET'], datetime.datetime.utcnow() + ttl) 50 | # policy.add_equals_condition('acl', 'public-read') 51 | uuid = uuid4_hex() 52 | # 服务端限定前缀增加安全性,至少能防止内容被篡改。 53 | # 只要使用相同的key再上传一遍,就能覆盖原内容,达到篡改效果。 54 | directory = f'media-${uuid}/' 55 | policy.add_starts_with_condition('key', directory) 56 | form_data = get_s3_client().presigned_post_policy(policy) 57 | return { 58 | 'host': s3_endpoint(), 59 | 'dir': directory, 60 | **form_data, 61 | } 62 | -------------------------------------------------------------------------------- /api_basebone/utils/uuid.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | 4 | def uuid4_hex(): 5 | return uuid.uuid4().hex 6 | -------------------------------------------------------------------------------- /api_basebone/utils/wechat/__init__.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.core.cache import cache 3 | from wechatpy.client import WeChatClient 4 | from wechatpy.client.api import WeChatWxa 5 | from wechatpy.session.redisstorage import RedisStorage 6 | from wechatpy.session.memorystorage import MemoryStorage 7 | import requests 8 | 9 | from django_redis import get_redis_connection 10 | 11 | session = RedisStorage(get_redis_connection())\ 12 | if settings.CACHES['default']['BACKEND'] == 'django_redis.cache.RedisCache'\ 13 | else MemoryStorage() 14 | 15 | 16 | def wrap(api, app_id, app_secret=None): 17 | return api(client=WeChatClient( 18 | appid=app_id, 19 | secret=app_secret or settings.WECHAT_APP_MAP[app_id]['appsecret'], 20 | session=session)) 21 | 22 | 23 | def wxa(app_id, app_secret=None): 24 | return wrap(WeChatWxa, app_id=app_id, app_secret=app_secret) 25 | 26 | 27 | def wechat_client(app_id, app_secret=None): 28 | return WeChatClient(app_id, 29 | secret=app_secret 30 | or settings.WECHAT_APP_MAP[app_id]['appsecret'], 31 | session=session) 32 | 33 | 34 | def send_robot_text(robot_id, message, mentioned_mobiles=[]): 35 | """发送企业微信机器人推送消息 36 | """ 37 | url = f'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={robot_id}' 38 | content = { 39 | "msgtype": "text", 40 | "text": { 41 | "content": message, 42 | "mentioned_mobile_list": mentioned_mobiles 43 | } 44 | } 45 | rsp = requests.post(url, json=content) 46 | return rsp 47 | -------------------------------------------------------------------------------- /api_basebone/utils/wechat/extension/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/api_basebone/utils/wechat/extension/__init__.py -------------------------------------------------------------------------------- /api_basebone/utils/wechat/logistics.py: -------------------------------------------------------------------------------- 1 | from api_basebone.utils.wechat.extension.logistics import WeChatLogistics 2 | from api_basebone.utils.wechat import wrap 3 | 4 | def create_logistics(app_id): 5 | return wrap(WeChatLogistics, app_id) 6 | 7 | 8 | -------------------------------------------------------------------------------- /bsm_config/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'bsm_config.apps.BsmConfigConfig' 2 | 3 | def config_loader(): 4 | from bsm_config.models import Admin 5 | configs = Admin.objects.all() 6 | return dict([(config.model, config.config) for config in configs]) 7 | -------------------------------------------------------------------------------- /bsm_config/admin.py: -------------------------------------------------------------------------------- 1 | from django.apps import apps 2 | from django.contrib import admin 3 | 4 | app_list = ['bsm_config'] 5 | 6 | for app_name in app_list: 7 | application = apps.get_app_config(app_name) 8 | admin.site.register(application.get_models()) 9 | -------------------------------------------------------------------------------- /bsm_config/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BsmConfigConfig(AppConfig): 5 | name = 'bsm_config' 6 | verbose_name = '系统配置' 7 | 8 | def ready(self): 9 | from bsm_config.bsm import functions 10 | from bsm_config import signals 11 | from api_basebone.restful.client.views import register_api 12 | register_api('auth', { 13 | 'permission': { 14 | 'actions': ['retrieve', 'list', 'set'] 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /bsm_config/bsm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/bsm_config/bsm/__init__.py -------------------------------------------------------------------------------- /bsm_config/exceptions.py: -------------------------------------------------------------------------------- 1 | from api_basebone.core.exceptions import BusinessException 2 | 3 | GET_CONFIG_ERROR = '10000' 4 | 5 | ERROR_PHRASES = { 6 | GET_CONFIG_ERROR: '获取配置异常', 7 | } 8 | 9 | 10 | def raise_business_exception(error_code, error_message=None, error_data=None): 11 | """抛出业务异常""" 12 | error_message = error_message if error_message else ERROR_PHRASES.get(error_code) 13 | raise BusinessException( 14 | error_code=error_code, 15 | error_message=error_message, 16 | error_data=error_data, 17 | error_app='bsm_config', 18 | ) 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-05-09 12:18 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('contenttypes', '0002_remove_content_type_name'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Menu', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('name', models.CharField(max_length=30, verbose_name='名称')), 21 | ('icon', models.CharField(blank=True, help_text='请使用AntDesign的图标名', max_length=100, null=True, verbose_name='图标名')), 22 | ('path', models.CharField(help_text='前端功能页面的路径', max_length=200, verbose_name='路径')), 23 | ('permission', models.CharField(help_text='格式有.', max_length=200, verbose_name='关联权限')), 24 | ('content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contenttypes.ContentType', verbose_name='关联模型')), 25 | ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='bsm_config.Menu', verbose_name='上级菜单')), 26 | ], 27 | options={ 28 | 'verbose_name': '导航菜单', 29 | 'verbose_name_plural': '导航菜单', 30 | }, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /bsm_config/migrations/0002_auto_20190509_2031.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-05-09 12:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='menu', 15 | name='content_type', 16 | ), 17 | migrations.AddField( 18 | model_name='menu', 19 | name='model', 20 | field=models.CharField(blank=True, help_text='格式为:__', max_length=200, null=True, verbose_name='关联模型'), 21 | ), 22 | migrations.AlterField( 23 | model_name='menu', 24 | name='icon', 25 | field=models.CharField(blank=True, help_text='请使用AntDesign的图标:https://ant.design/components/icon-cn/', max_length=100, null=True, verbose_name='图标名'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /bsm_config/migrations/0003_auto_20190509_2125.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-05-09 13:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0002_auto_20190509_2031'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='menu', 15 | name='path', 16 | ), 17 | migrations.AddField( 18 | model_name='menu', 19 | name='page', 20 | field=models.CharField(default='list', help_text='前端功能页面的标识', max_length=200, verbose_name='页面'), 21 | ), 22 | migrations.AlterField( 23 | model_name='menu', 24 | name='permission', 25 | field=models.CharField(blank=True, help_text='格式有.', max_length=200, null=True, verbose_name='关联权限'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /bsm_config/migrations/0004_auto_20190510_1820.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-05-10 10:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0003_auto_20190509_2125'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='menu', 15 | name='sequence', 16 | field=models.PositiveIntegerField(default=0, help_text='数值越小,排列越前', verbose_name='排序'), 17 | ), 18 | migrations.AlterField( 19 | model_name='menu', 20 | name='page', 21 | field=models.CharField(blank=True, default='list', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /bsm_config/migrations/0005_auto_20190709_1338.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-07-09 05:38 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('bsm_config', '0004_auto_20190510_1820'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='menu', 16 | name='parent', 17 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='bsm_config.Menu', verbose_name='上级菜单'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /bsm_config/migrations/0006_auto_20190712_1658.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.3 on 2019-07-12 08:58 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0005_auto_20190709_1338'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='sequence', 16 | field=models.PositiveIntegerField(default=1000, help_text='数值越小,排列越前', verbose_name='排序'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0007_auto_20200102_1616.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2 on 2020-01-02 08:16 2 | 3 | import api_basebone.core.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('bsm_config', '0006_auto_20190712_1658'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Admin', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('model', models.CharField(max_length=30, verbose_name='模型名称')), 19 | ('config', api_basebone.core.fields.JSONField(default={})), 20 | ], 21 | options={ 22 | 'verbose_name': 'Admin配置', 23 | 'verbose_name_plural': 'Admin配置', 24 | }, 25 | ), 26 | migrations.AlterField( 27 | model_name='menu', 28 | name='page', 29 | field=models.CharField(blank=True, choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板']], default='list', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面'), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /bsm_config/migrations/0008_auto_20200120_1416.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2 on 2020-01-20 06:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0007_auto_20200102_1616'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='sequence', 16 | field=models.IntegerField(default=1000, help_text='数值越小,排列越前', verbose_name='排序'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0009_auto_20200226_1311.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-02-26 05:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0008_auto_20200120_1416'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Setting', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('key', models.CharField(max_length=30, unique=True, verbose_name='配置键')), 18 | ('value', models.CharField(max_length=255, verbose_name='配置值')), 19 | ('type', models.CharField(choices=[('string', '字符串'), ('text', '文本'), ('integer', '整型'), ('float', '浮点数'), ('decimal', '小数'), ('bool', 'Boolean值'), ('time', '时间'), ('date', '日期'), ('datetime', '日期时间'), ('image', '图片'), ('file', '文件')], default='string', max_length=30, verbose_name='键类型')), 20 | ('display_name', models.CharField(max_length=30, verbose_name='中文名')), 21 | ], 22 | options={ 23 | 'verbose_name': '网站配置', 24 | 'verbose_name_plural': '网站配置', 25 | }, 26 | ), 27 | migrations.AddField( 28 | model_name='menu', 29 | name='path', 30 | field=models.CharField(blank=True, max_length=255, null=True, verbose_name='自定义路径'), 31 | ), 32 | migrations.AlterField( 33 | model_name='menu', 34 | name='page', 35 | field=models.CharField(blank=True, choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面']], default='list', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面'), 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /bsm_config/migrations/0010_auto_20200226_1514.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-02-26 07:14 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0009_auto_20200226_1311'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='setting', 15 | name='value', 16 | field=models.CharField(blank=True, max_length=255, null=True, verbose_name='配置值'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0011_auto_20200410_2247.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-04-10 14:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0010_auto_20200226_1514'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='name', 16 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='名称'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0012_auto_20200417_1847.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2 on 2020-04-17 10:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0011_auto_20200410_2247'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='admin', 15 | name='model', 16 | field=models.CharField(max_length=100, verbose_name='模型名称'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0013_auto_20200418_1816.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-04-18 10:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0012_auto_20200417_1847'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='setting', 15 | name='value', 16 | field=models.CharField(max_length=255, null=True, verbose_name='配置值'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0014_auto_20200422_1925.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-04-22 11:25 2 | 3 | import bsm_config.models 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('bsm_config', '0013_auto_20200418_1816'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='menu', 16 | name='model', 17 | field=models.CharField(help_text='格式为:__', max_length=200, null=True, verbose_name='关联模型'), 18 | ), 19 | migrations.AlterField( 20 | model_name='menu', 21 | name='name', 22 | field=models.CharField(max_length=30, null=True, verbose_name='名称'), 23 | ), 24 | migrations.AlterField( 25 | model_name='menu', 26 | name='sequence', 27 | field=models.IntegerField(help_text='数值越小,排列越前', unique=True, verbose_name='排序'), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /bsm_config/migrations/0015_auto_20200423_1231.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-04-23 04:31 2 | 3 | import bsm_config.models 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('bsm_config', '0014_auto_20200422_1925'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='menu', 16 | name='sequence', 17 | field=models.IntegerField(default=1000, help_text='数值越小,排列越前', verbose_name='排序'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /bsm_config/migrations/0016_auto_20200429_1117.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2 on 2020-04-29 03:17 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0015_auto_20200423_1231'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='page', 16 | field=models.CharField(blank=True, choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面'], ['chart', '自定义图表']], default='list', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0017_auto_20200511_1927.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-05-11 11:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | def update_menu_type(apps, schema_editor): 7 | print('更新有children的菜单类型为菜单组') 8 | Menu = apps.get_app_config('bsm_config').get_model('Menu') 9 | Menu.objects.using(schema_editor.connection.alias).filter(children__isnull=False).update(type='group') 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | dependencies = [ 15 | ('bsm_config', '0016_auto_20200429_1117'), 16 | ] 17 | 18 | operations = [ 19 | migrations.AddField( 20 | model_name='menu', 21 | name='type', 22 | field=models.CharField(choices=[('item', '菜单项'), ('group', '菜单组')], default='item', max_length=20, verbose_name='菜单类型'), 23 | ), 24 | migrations.AlterField( 25 | model_name='menu', 26 | name='icon', 27 | field=models.CharField(blank=True, max_length=100, null=True, verbose_name='图标'), 28 | ), 29 | migrations.AlterField( 30 | model_name='menu', 31 | name='model', 32 | field=models.CharField(max_length=200, null=True, verbose_name='关联模型'), 33 | ), 34 | migrations.AlterField( 35 | model_name='menu', 36 | name='name', 37 | field=models.CharField(blank=True, max_length=30, null=True, verbose_name='名称'), 38 | ), 39 | migrations.AlterField( 40 | model_name='menu', 41 | name='page', 42 | field=models.CharField(choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面'], ['chart', '自定义图表']], default='', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面'), 43 | ), 44 | migrations.RunPython(update_menu_type, reverse_code=migrations.RunPython.noop) 45 | ] 46 | -------------------------------------------------------------------------------- /bsm_config/migrations/0018_menu_groups.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-05-14 06:04 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('auth', '0011_update_proxy_permissions'), 10 | ('bsm_config', '0017_auto_20200511_1927'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='menu', 16 | name='groups', 17 | field=models.ManyToManyField(blank=True, help_text='设置可查看此菜单的角色', related_name='groups', to='auth.Group', verbose_name='关联角色'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /bsm_config/migrations/0019_setting_is_admin.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-05-27 08:46 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0018_menu_groups'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='setting', 15 | name='is_admin', 16 | field=models.BooleanField(default=False, verbose_name='仅管理员可看'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0020_auto_20200530_1520.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-05-30 07:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0019_setting_is_admin'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='page', 16 | field=models.CharField(choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面'], ['chart', '自定义图表'], ['iframe', 'Iframe页面']], default='', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0021_auto_20200531_2305.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-05-31 15:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0020_auto_20200530_1520'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='setting', 15 | name='value', 16 | field=models.TextField(null=True, verbose_name='配置值'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0022_auto_20200602_1629.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-06-02 08:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0021_auto_20200531_2305'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='groups', 16 | field=models.ManyToManyField(blank=True, help_text='设置可查看此菜单的角色', related_name='menus', to='auth.Group', verbose_name='关联角色'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0023_setting_help_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-06-10 07:13 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0022_auto_20200602_1629'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='setting', 15 | name='help_text', 16 | field=models.CharField(blank=True, max_length=225, null=True, verbose_name='提示信息'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0024_setting_extra_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-06-22 07:50 2 | 3 | import api_basebone.core.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('bsm_config', '0023_setting_help_text'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='setting', 16 | name='extra_data', 17 | field=api_basebone.core.fields.JSONField(blank=True, default={}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /bsm_config/migrations/0027_auto_20200822_2031.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-08-22 12:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0026_auto_20200722_2308'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='admin', 15 | name='model', 16 | field=models.CharField(max_length=100, unique=True, verbose_name='模型名称'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0028_auto_20200925_1216.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-09-25 04:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0027_auto_20200822_2031'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='page', 16 | field=models.CharField(choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面'], ['chart', '自定义图表'], ['iframe', 'Iframe页面'], ['map', '地图页面']], default='', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0029_auto_20200926_0010.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-09-25 16:10 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0028_auto_20200925_1216'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='page', 16 | field=models.CharField(choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面'], ['chart', '自定义图表'], ['iframe', 'Iframe页面']], default='', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0030_fieldadmin_fieldpermission.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-10-28 08:02 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('auth', '0011_update_proxy_permissions'), 11 | ('bsm_config', '0029_auto_20200926_0010'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='FieldAdmin', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('field', models.CharField(max_length=150, verbose_name='字段名')), 20 | ('admin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bsm_config.Admin', verbose_name='关联Admin')), 21 | ], 22 | options={ 23 | 'verbose_name': 'Admin字段配置', 24 | 'verbose_name_plural': 'Admin字段配置', 25 | 'unique_together': {('admin', 'field')}, 26 | }, 27 | ), 28 | migrations.CreateModel( 29 | name='FieldPermission', 30 | fields=[ 31 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 32 | ('read', models.BooleanField(default=True, verbose_name='读')), 33 | ('write', models.BooleanField(default=True, verbose_name='写')), 34 | ('field_admin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bsm_config.FieldAdmin', verbose_name='关联字段')), 35 | ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group', verbose_name='角色')), 36 | ], 37 | options={ 38 | 'verbose_name': '字段权限配置', 39 | 'verbose_name_plural': '字段权限配置', 40 | 'unique_together': {('field_admin', 'group')}, 41 | }, 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /bsm_config/migrations/0031_auto_20201126_1344.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-11-26 05:44 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('puzzle', '0002_auto_20201125_0722'), 11 | ('bsm_config', '0030_fieldadmin_fieldpermission'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='menu', 17 | name='puzzle', 18 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='menus', to='puzzle.Page', verbose_name='指向页面'), 19 | ), 20 | migrations.AlterField( 21 | model_name='menu', 22 | name='page', 23 | field=models.CharField(choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面'], ['chart', '自定义图表'], ['iframe', 'Iframe页面']], default='', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面类型'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /bsm_config/migrations/0031_auto_20201203_1222.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-12-03 04:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0030_fieldadmin_fieldpermission'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='groups', 16 | field=models.ManyToManyField(blank=True, help_text='可查看此菜单的角色,不设置默认所有角色可查看此菜单', related_name='menus', to='auth.Group', verbose_name='关联角色'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0032_auto_20201126_1802.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-11-26 18:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0031_auto_20201126_1344'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='page', 16 | field=models.CharField(choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面'], ['puzzle', '仪表盘'], ['iframe', 'Iframe页面']], default='', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面类型'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0032_auto_20201203_0530.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-12-03 05:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0031_auto_20201203_1222'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='groups', 16 | field=models.ManyToManyField(blank=True, help_text='限制可查看此菜单的角色,留空则不限制', related_name='menus', to='auth.Group', verbose_name='可见角色'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0033_auto_20201203_1558.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-12-03 15:58 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0032_auto_20201203_0530'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='page', 16 | field=models.CharField(choices=[['list', '列表页'], ['detail', '详情页'], ['adminConfig', '页面配置面板'], ['auto', '自定义页面'], ['iframe', 'Iframe页面']], default='', help_text='前端功能页面的标识', max_length=200, null=True, verbose_name='页面类型'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0034_merge_20201205_1249.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-12-05 12:49 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0032_auto_20201126_1802'), 10 | ('bsm_config', '0033_auto_20201203_1558'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /bsm_config/migrations/0035_menu_view.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.17 on 2021-03-17 15:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0034_merge_20201205_1249'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='menu', 15 | name='view', 16 | field=models.CharField(help_text='自定义视图名,非模型的默认列表', max_length=200, null=True, verbose_name='视图'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/0036_auto_20210320_1712.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.17 on 2021-03-20 09:12 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('bsm_config', '0035_menu_view'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='view', 16 | field=models.CharField(blank=True, help_text='自定义视图名,非模型的默认列表', max_length=200, null=True, verbose_name='视图'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /bsm_config/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/bsm_config/migrations/__init__.py -------------------------------------------------------------------------------- /bsm_config/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /bsm_config/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /lightning/__init__.py: -------------------------------------------------------------------------------- 1 | from api_basebone.permissions import BasePermission 2 | default_app_config = 'lightning.apps.LightningConfig' 3 | 4 | APPS = [ 5 | 'guardian', 6 | 'api_basebone', 7 | 'bsm_config', 8 | 'shield', 9 | 'storage', 10 | 'puzzle', 11 | 'lightning', 12 | 'rest_framework', 13 | ] 14 | 15 | FUNC_SCENE_UNLIMIT = 'unlimit' 16 | FUNC_SCENE_INLINE = 'inline' 17 | FUNC_SCENE_BATCH = 'batch' 18 | 19 | FUNC_DATA_TYPE_STRING = 'string' 20 | FUNC_DATA_TYPE_INT = 'integer' 21 | FUNC_DATA_TYPE_DECIMAL = 'decimal' 22 | FUNC_DATA_TYPE_BOOL = 'bool' 23 | FUNC_DATA_TYPE_REF = 'ref' 24 | FUNC_DATA_TYPE_DATE = 'date' 25 | FUNC_DATA_TYPE_IMAGE = 'image' 26 | -------------------------------------------------------------------------------- /lightning/admin.py: -------------------------------------------------------------------------------- 1 | from api_basebone.core.admin import BSMAdmin 2 | 3 | Admin = BSMAdmin -------------------------------------------------------------------------------- /lightning/apps.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import logging 4 | from django.contrib.auth import get_user_model 5 | from django.apps import AppConfig 6 | from django.conf import settings 7 | from api_basebone.utils import module 8 | 9 | log = logging.getLogger('lightning') 10 | 11 | 12 | class LightningConfig(AppConfig): 13 | name = 'lightning' 14 | 15 | def ready(self): 16 | user_model = get_user_model() 17 | path = user_model._meta.app_config.name + '.bsm.' + module.BSM_FORM 18 | from .bsm import user_forms 19 | sys.modules[path] = user_forms 20 | 21 | apps = settings.INSTALLED_APPS 22 | for app in apps: 23 | try: 24 | # TODO INSTALLED_APPS 里配的内容不一定是module,还不一定是字符串。提前导入也会改变导入顺序,使一些云函数的覆盖逻辑出错。 25 | app_module = __import__(app) 26 | path = app_module.__path__[0] 27 | functions_path = os.path.join(path, 'functions') 28 | signal_path = os.path.join(path, 'signals') 29 | if os.path.exists(functions_path + '.py') or os.path.exists(functions_path + '.pyc'): 30 | __import__('.'.join([app, 'functions'])) 31 | if os.path.exists(signal_path + '.py') or os.path.exists(signal_path + '.pyc'): 32 | __import__('.'.join([app, 'signals'])) 33 | except: 34 | log.warning(f'自动发现脚本发生异常: {functions_path}', exc_info=True) 35 | pass 36 | -------------------------------------------------------------------------------- /lightning/bsm/__init__.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from api_basebone.export.specs import FieldType 3 | 4 | 5 | class UserGMeta: 6 | title_field = getattr(settings, 'USER_MODEL_TITLE_FIELD', 'fullname') 7 | computed_fields = [ 8 | {'name': 'fullname', 'type': FieldType.STRING, 'display_name': '名字'} 9 | ] 10 | exclude_fields = ['password'] 11 | field_form_config = {'password': {'required': False}} 12 | 13 | -------------------------------------------------------------------------------- /lightning/const.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.contrib.auth import get_user_model 3 | # 暂不支持菜单组,嵌套子菜单 4 | User = get_user_model() 5 | 6 | DEFAULT_MENU = [ 7 | # {"name":"仪表盘","children":[],"model":None,"page":"chart","icon":"line-chart"}, 8 | { 9 | "name":"权限管理", 10 | "model":"", 11 | "type": "group", 12 | "icon":"lock", 13 | "children":[ 14 | {"name":"账号管理","children":[],"model":f"{User._meta.app_label}__{User._meta.model_name}","page":"list","icon":""}, 15 | {"name":"角色管理","children":[],"model":"auth__group","page":"list","icon":""}, 16 | {"name":"资源权限","children":[],"model":"auth__permission","page":"list","icon":""}, 17 | ] 18 | }, 19 | { 20 | "name":"开发管理", 21 | "model":"", 22 | "type": "group", 23 | "icon":"tool", 24 | "children":[ 25 | {"name":"菜单管理","children":[],"model":"bsm_config__menu","page":"list","icon":"menu",}, 26 | {"name":"页面配置","children":[],"model":"","page":"adminConfig","icon":"block__outlined",}, 27 | # {"name":"API管理","children":[],"model":"api_db__api","page":"list","icon":"api"}, 28 | # {"name":"触发器管理","children":[],"model":"trigger_core__trigger","page":"list","icon":"fork"}, 29 | ] 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /lightning/decorators.py: -------------------------------------------------------------------------------- 1 | from api_basebone.core.admin import register 2 | from api_basebone.restful.funcs import bsm_func 3 | 4 | lightning_admin = register 5 | lightning_func = bsm_func -------------------------------------------------------------------------------- /lightning/exceptions.py: -------------------------------------------------------------------------------- 1 | from api_basebone.core.exceptions import BusinessException -------------------------------------------------------------------------------- /lightning/fields.py: -------------------------------------------------------------------------------- 1 | from api_basebone.core import fields 2 | 3 | JSONField = fields.JSONField 4 | ObjectField = fields.ObjectField 5 | ArrayField = fields.ArrayField 6 | ImageURLField = fields.BoneImageUrlField 7 | FileURLField = fields.BoneFileUrlField 8 | RichTextField = fields.BoneRichTextField 9 | TimestampField = fields.BoneTimeStampField -------------------------------------------------------------------------------- /lightning/form.py: -------------------------------------------------------------------------------- 1 | class TextInput(dict): 2 | def __init__(self, name, params=None): 3 | super().__init__({'name': name, 'widget': type(self).__name__, 'params': params or {}}) 4 | 5 | 6 | class InnerTable(TextInput): 7 | def __init__(self, name, fields=None, display=None, can_add=True): 8 | params = {'canAdd': can_add} 9 | if fields is not None: 10 | params['fields'] = fields 11 | if display is not None: 12 | params['display'] = display 13 | super().__init__(name, params=params) 14 | 15 | 16 | class InlineForm(TextInput): 17 | def __init__(self, name, fields=None, can_add=True): 18 | params = {'canAdd': can_add} 19 | if fields is not None: 20 | params['fields'] = fields 21 | super().__init__(name, params=params) 22 | 23 | 24 | class Radio(TextInput): 25 | pass 26 | 27 | 28 | class Gallery(TextInput): 29 | pass 30 | 31 | 32 | class Cascader(TextInput): 33 | pass 34 | 35 | -------------------------------------------------------------------------------- /lightning/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/management/__init__.py -------------------------------------------------------------------------------- /lightning/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/management/commands/__init__.py -------------------------------------------------------------------------------- /lightning/management/commands/light.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand, CommandError 2 | from bsm_config.models import Menu, Admin 3 | 4 | from lightning.services import generate_configs 5 | 6 | class Command(BaseCommand): 7 | help = 'Generate lightning Menu and Admin pages' 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument('apps', nargs='+', type=str) 11 | 12 | def handle(self, *args, **options): 13 | if not options['apps']: 14 | self.stdout.write(self.style.ERROR('app label is required for lighting generation')) 15 | generate_configs(options['apps']) 16 | self.stdout.write(self.style.SUCCESS('lightning admin config successfully generated!')) 17 | 18 | -------------------------------------------------------------------------------- /lightning/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/migrations/__init__.py -------------------------------------------------------------------------------- /lightning/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /lightning/signals.py: -------------------------------------------------------------------------------- 1 | from api_basebone.signals import post_bsm_create as post_lightning_create 2 | from api_basebone.signals import before_bsm_create as pre_lightning_create 3 | from api_basebone.signals import post_bsm_delete as post_lightning_delete 4 | from api_basebone.signals import before_bsm_delete as pre_lightning_delete 5 | -------------------------------------------------------------------------------- /lightning/static/lightning/110.eae832ea.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[110],{scEK:function(n,r,t){(function(n){n(t("VrN/"))})((function(n){"use strict";n.defineMode("http",(function(){function n(n,r){return n.skipToEnd(),r.cur=o,"error"}function r(r,e){return r.match(/^HTTP\/\d\.\d/)?(e.cur=t,"keyword"):r.match(/^[A-Z]+/)&&/[ \t]/.test(r.peek())?(e.cur=u,"keyword"):n(r,e)}function t(r,t){var u=r.match(/^\d+/);if(!u)return n(r,t);t.cur=e;var i=Number(u[0]);return i>=100&&i<200?"positive informational":i>=200&&i<300?"positive success":i>=300&&i<400?"positive redirect":i>=400&&i<500?"negative client-error":i>=500&&i<600?"negative server-error":"error"}function e(n,r){return n.skipToEnd(),r.cur=o,null}function u(n,r){return n.eatWhile(/\S/),r.cur=i,"string-2"}function i(r,t){return r.match(/^HTTP\/\d\.\d$/)?(t.cur=o,"keyword"):n(r,t)}function o(n){return n.sol()&&!n.eat(/[ \t]/)?n.match(/^.*?:/)?"atom":(n.skipToEnd(),"error"):(n.skipToEnd(),"string")}function c(n){return n.skipToEnd(),null}return{token:function(n,r){var t=r.cur;return t!=o&&t!=c&&n.eatSpace()?null:t(n,r)},blankLine:function(n){n.cur=c},startState:function(){return{cur:r}}}})),n.defineMIME("message/http","http")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/116.77bb1da1.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[116],{ztbM:function(e,t,a){(function(e){e(a("VrN/"))})((function(e){"use strict";e.defineMode("mathematica",(function(e,t){var a="[a-zA-Z\\$][a-zA-Z0-9\\$]*",n="(?:\\d+)",m="(?:\\.\\d+|\\d+\\.\\d*|\\d+)",c="(?:\\.\\w+|\\w+\\.\\w*|\\w+)",o="(?:`(?:`?"+m+")?)",z=new RegExp("(?:"+n+"(?:\\^\\^"+c+o+"?(?:\\*\\^[+-]?\\d+)?))"),r=new RegExp("(?:"+m+o+"?(?:\\*\\^[+-]?\\d+)?)"),i=new RegExp("(?:`?)(?:"+a+")(?:`(?:"+a+"))*(?:`?)");function A(e,t){var a;return a=e.next(),'"'===a?(t.tokenize=Z,t.tokenize(e,t)):"("===a&&e.eat("*")?(t.commentLevel++,t.tokenize=$,t.tokenize(e,t)):(e.backUp(1),e.match(z,!0,!1)||e.match(r,!0,!1)?"number":e.match(/(?:In|Out)\[[0-9]*\]/,!0,!1)?"atom":e.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::usage)/,!0,!1)?"meta":e.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::[a-zA-Z\$][a-zA-Z0-9\$]*):?/,!0,!1)?"string-2":e.match(/([a-zA-Z\$][a-zA-Z0-9\$]*\s*:)(?:(?:[a-zA-Z\$][a-zA-Z0-9\$]*)|(?:[^:=>~@\^\&\*\)\[\]'\?,\|])).*/,!0,!1)||e.match(/[a-zA-Z\$][a-zA-Z0-9\$]*_+[a-zA-Z\$][a-zA-Z0-9\$]*/,!0,!1)||e.match(/[a-zA-Z\$][a-zA-Z0-9\$]*_+/,!0,!1)||e.match(/_+[a-zA-Z\$][a-zA-Z0-9\$]*/,!0,!1)?"variable-2":e.match(/\\\[[a-zA-Z\$][a-zA-Z0-9\$]*\]/,!0,!1)?"variable-3":e.match(/(?:\[|\]|{|}|\(|\))/,!0,!1)?"bracket":e.match(/(?:#[a-zA-Z\$][a-zA-Z0-9\$]*|#+[0-9]?)/,!0,!1)?"variable-2":e.match(i,!0,!1)?"keyword":e.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%)/,!0,!1)?"operator":(e.next(),"error"))}function Z(e,t){var a,n=!1,m=!1;while(null!=(a=e.next())){if('"'===a&&!m){n=!0;break}m=!m&&"\\"===a}return n&&!m&&(t.tokenize=A),"string"}function $(e,t){var a,n;while(t.commentLevel>0&&null!=(n=e.next()))"("===a&&"*"===n&&t.commentLevel++,"*"===a&&")"===n&&t.commentLevel--,a=n;return t.commentLevel<=0&&(t.tokenize=A),"comment"}return{startState:function(){return{tokenize:A,commentLevel:0}},token:function(e,t){return e.eatSpace()?null:t.tokenize(e,t)},blockCommentStart:"(*",blockCommentEnd:"*)"}})),e.defineMIME("text/x-mathematica",{name:"mathematica"})}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/117.b146effd.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[117],{"6mA5":function(e,n,r){(function(e){e(r("VrN/"))})((function(e){"use strict";var n=["From","Sender","Reply-To","To","Cc","Bcc","Message-ID","In-Reply-To","References","Resent-From","Resent-Sender","Resent-To","Resent-Cc","Resent-Bcc","Resent-Message-ID","Return-Path","Received"],r=["Date","Subject","Comments","Keywords","Resent-Date"];e.registerHelper("hintWords","mbox",n.concat(r));var t=/^[ \t]/,a=/^From /,i=new RegExp("^("+n.join("|")+"): "),o=new RegExp("^("+r.join("|")+"): "),d=/^[^:]+:/,s=/^[^ ]+@[^ ]+/,c=/^.*?(?=[^ ]+?@[^ ]+)/,m=/^<.*?>/,u=/^.*?(?=<.*>)/;function l(e){return"Subject"===e?"header":"string"}function p(e,n){if(e.sol()){if(n.inSeparator=!1,n.inHeader&&e.match(t))return null;if(n.inHeader=!1,n.header=null,e.match(a))return n.inHeaders=!0,n.inSeparator=!0,"atom";var r,p=!1;return(r=e.match(o))||(p=!0)&&(r=e.match(i))?(n.inHeaders=!0,n.inHeader=!0,n.emailPermitted=p,n.header=r[1],"atom"):n.inHeaders&&(r=e.match(d))?(n.inHeader=!0,n.emailPermitted=!0,n.header=r[1],"atom"):(n.inHeaders=!1,e.skipToEnd(),null)}if(n.inSeparator)return e.match(s)?"link":(e.match(c)||e.skipToEnd(),"atom");if(n.inHeader){var f=l(n.header);if(n.emailPermitted){if(e.match(m))return f+" link";if(e.match(u))return f}return e.skipToEnd(),f}return e.skipToEnd(),null}e.defineMode("mbox",(function(){return{startState:function(){return{inSeparator:!1,inHeader:!1,emailPermitted:!1,header:null,inHeaders:!1}},token:p,blankLine:function(e){e.inHeaders=e.inSeparator=e.inHeader=!1}}})),e.defineMIME("application/mbox","mbox")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/122.352eb326.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[122],{Q7su:function(e,t,n){(function(e){e(n("VrN/"))})((function(e){"use strict";e.defineMode("mumps",(function(){function e(e){return new RegExp("^(("+e.join(")|(")+"))\\b","i")}var t=new RegExp("^[\\+\\-\\*/&#!_?\\\\<>=\\'\\[\\]]"),n=new RegExp("^(('=)|(<=)|(>=)|('>)|('<)|([[)|(]])|(^$))"),o=new RegExp("^[\\.,:]"),$=new RegExp("[()]"),a=new RegExp("^[%A-Za-z][A-Za-z0-9]*"),r=["break","close","do","else","for","goto","halt","hang","if","job","kill","lock","merge","new","open","quit","read","set","tcommit","trollback","tstart","use","view","write","xecute","b","c","d","e","f","g","h","i","j","k","l","m","n","o","q","r","s","tc","tro","ts","u","v","w","x"],c=["\\$ascii","\\$char","\\$data","\\$ecode","\\$estack","\\$etrap","\\$extract","\\$find","\\$fnumber","\\$get","\\$horolog","\\$io","\\$increment","\\$job","\\$justify","\\$length","\\$name","\\$next","\\$order","\\$piece","\\$qlength","\\$qsubscript","\\$query","\\$quit","\\$random","\\$reverse","\\$select","\\$stack","\\$test","\\$text","\\$translate","\\$view","\\$x","\\$y","\\$a","\\$c","\\$d","\\$e","\\$ec","\\$es","\\$et","\\$f","\\$fn","\\$g","\\$h","\\$i","\\$j","\\$l","\\$n","\\$na","\\$o","\\$p","\\$q","\\$ql","\\$qs","\\$r","\\$re","\\$s","\\$st","\\$t","\\$tr","\\$v","\\$z"],i=e(c),m=e(r);function d(e,r){e.sol()&&(r.label=!0,r.commandMode=0);var c=e.peek();return" "==c||"\t"==c?(r.label=!1,0==r.commandMode?r.commandMode=1:(r.commandMode<0||2==r.commandMode)&&(r.commandMode=0)):"."!=c&&r.commandMode>0&&(r.commandMode=":"==c?-1:2),"("!==c&&"\t"!==c||(r.label=!1),";"===c?(e.skipToEnd(),"comment"):e.match(/^[-+]?\d+(\.\d+)?([eE][-+]?\d+)?/)?"number":'"'==c?e.skipTo('"')?(e.next(),"string"):(e.skipToEnd(),"error"):e.match(n)||e.match(t)?"operator":e.match(o)?null:$.test(c)?(e.next(),"bracket"):r.commandMode>0&&e.match(m)?"variable-2":e.match(i)?"builtin":e.match(a)?"variable":"$"===c||"^"===c?(e.next(),"builtin"):"@"===c?(e.next(),"string-2"):/[\w%]/.test(c)?(e.eatWhile(/[\w%]/),"variable"):(e.next(),"error")}return{startState:function(){return{label:!1,commandMode:0}},token:function(e,t){var n=d(e,t);return t.label?"tag":n}}})),e.defineMIME("text/x-mumps","mumps")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/128.cab961a4.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[128],{ZGb1:function(e,t,n){(function(e){e(n("VrN/"),n("+dQi"))})((function(e){"use strict";e.defineMode("pegjs",(function(t){var n=e.getMode(t,"javascript");function r(e){return e.match(/^[a-zA-Z_][a-zA-Z0-9_]*/)}return{startState:function(){return{inString:!1,stringType:null,inComment:!1,inCharacterClass:!1,braced:0,lhs:!0,localState:null}},token:function(t,i){if(i.inString||i.inComment||'"'!=t.peek()&&"'"!=t.peek()||(i.stringType=t.peek(),t.next(),i.inString=!0),i.inString||i.inComment||!t.match("/*")||(i.inComment=!0),i.inString){while(i.inString&&!t.eol())t.peek()===i.stringType?(t.next(),i.inString=!1):"\\"===t.peek()?(t.next(),t.next()):t.match(/^.[^\\\"\']*/);return i.lhs?"property string":"string"}if(i.inComment){while(i.inComment&&!t.eol())t.match("*/")?i.inComment=!1:t.match(/^.[^\*]*/);return"comment"}if(i.inCharacterClass)while(i.inCharacterClass&&!t.eol())t.match(/^[^\]\\]+/)||t.match(/^\\./)||(i.inCharacterClass=!1);else{if("["===t.peek())return t.next(),i.inCharacterClass=!0,"bracket";if(t.match("//"))return t.skipToEnd(),"comment";if(i.braced||"{"===t.peek()){null===i.localState&&(i.localState=e.startState(n));var a=n.token(t,i.localState),c=t.current();if(!a)for(var o=0;o\=|\>|\=\=|\&\&|\|\|)/;return{startState:function(){return{controlFlow:!1,macroParameters:!1,section:!1}},token:function(i,o){var p=i.peek();if("#"==p)return i.skipToEnd(),"comment";if(i.sol()){if(i.match(e))return"header";if(i.match(t))return"atom"}if(i.match(/^\$\w+/))return"def";if(i.match(/^\$\{\w+\}/))return"def";if(i.match(a))return"keyword";if(i.match(n))return o.controlFlow=!0,"keyword";if(o.controlFlow){if(i.match(c))return"operator";if(i.match(/^(\d+)/))return"number";i.eol()&&(o.controlFlow=!1)}if(i.match(r))return i.eol()&&(o.controlFlow=!1),"number";if(i.match(/^%[\w]+/))return i.match("(")&&(o.macroParameters=!0),"keyword";if(o.macroParameters){if(i.match(/^\d+/))return"number";if(i.match(")"))return o.macroParameters=!1,"keyword"}return i.match(/^%\{\??[\w \-\:\!]+\}/)?(i.eol()&&(o.controlFlow=!1),"def"):(i.next(),null)}}})),r.defineMIME("text/x-rpm-spec","rpm-spec")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/14.3b6e9a13.chunk.css: -------------------------------------------------------------------------------- 1 | .handle____zXwB{position:absolute;top:300px;right:100%;z-index:0;display:flex;align-items:center;justify-content:center;width:48px;height:48px;font-size:16px;text-align:center;background:#1890ff;border-radius:2px 0 0 2px;cursor:pointer;pointer-events:auto} -------------------------------------------------------------------------------- /lightning/static/lightning/14.d4028317.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[14],{"7gdK":function(e,t,n){"use strict";(function(e){n("QV15");var a=n("DAsn"),r=(n("eE3J"),n("GSZB")),c=n("tJVT"),l=n("q1tI"),u=n.n(l),o=n("/MKj"),i=n("fVK0"),s=n("jfUH"),d=n("yPhJ"),f=n("txBY"),m=n("ywuK"),b=n.n(m),h=n("Utjr"),j=Object(h["d"])((function(){return n.e(21).then(n.bind(null,"Y3Wu"))})),E=function(e){var t=e.model,n=e.defaultTab,o=e.updateAdmin,i=Object(l["useState"])(!1),s=Object(c["a"])(i,2),f=s[0],m=s[1],h=Object(l["useState"])(!1),E=Object(c["a"])(h,2),w=E[0],p=E[1],v=Object(d["useAdminEditor"])({modelName:t}),y=v.check,O=v.reset,A=v.verifys,S=v.conf,k=v.tabs,K=function(){m(!0),p((function(e){return!e}))};return u.a.createElement(a["a"],{width:900,visible:w,onClose:K,placement:"right",handler:u.a.createElement("div",{className:b.a.handle,onClick:K},u.a.createElement(r["a"],{type:w?"close":"layout",style:{color:"#fff",fontSize:20}})),style:{zIndex:999},footer:u.a.createElement(d["AdminEditorButtons"],{conf:S,check:y,reset:O,verifys:A,afterSubmit:K,updateAdmin:o,modelName:t})},f&&u.a.createElement(j,{defaultTab:n,tabs:k}))},w=e.flow(Object(s["g"])("shouldShow"),Object(s["f"])((function(t){var n=t.model,a=t.currentUser.permissions;return{shouldShow:e.includes(a,i["b"].CHANGE_ADMIN)&&n&&Object(s["cb"])(n)}})),Object(o["c"])((function(e){var t=e.user.currentUser;return{currentUser:t}})),f["a"]);t["a"]=w((function(e){var t=e.model,n=Object(s["yb"])(t);return n?u.a.createElement(E,e):null}))}).call(this,n("LvDl"))},HELq:function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),r=n.n(a),c=n("IAeB"),l=n("7gdK");t["default"]=function(e){var t=e.match.params.model;return r.a.createElement(r.a.Fragment,null,r.a.createElement(c["b"],e),r.a.createElement(l["a"],{model:t,key:t,defaultTab:"detail"}))}},ywuK:function(e,t,n){e.exports={handle:"handle____zXwB"}}},0,[21]]); -------------------------------------------------------------------------------- /lightning/static/lightning/144.5ef202da.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[144],{"1dRh":function(e,n,t){(function(e){e(t("VrN/"))})((function(e){"use strict";e.defineMode("sieve",(function(e){function n(e){for(var n={},t=e.split(" "),i=0;i=@%|&?!.,:;^]/,n=/true|false|nil|self|super|thisContext/,a=function(e,t){this.next=e,this.parent=t},i=function(e,t,n){this.name=e,this.context=t,this.eos=n},r=function(){this.context=new a(o,null),this.expectVariable=!0,this.indentation=0,this.userIndentationDelta=0};r.prototype.userIndent=function(t){this.userIndentationDelta=t>0?t/e.indentUnit-this.indentation:0};var o=function(e,r,o){var x=new i(null,r,!1),d=e.next();return'"'===d?x=s(e,new a(s,r)):"'"===d?x=u(e,new a(u,r)):"#"===d?"'"===e.peek()?(e.next(),x=c(e,new a(c,r))):e.eatWhile(/[^\s.{}\[\]()]/)?x.name="string-2":x.name="meta":"$"===d?("<"===e.next()&&(e.eatWhile(/[^\s>]/),e.next()),x.name="string-2"):"|"===d&&o.expectVariable?x.context=new a(l,r):/[\[\]{}()]/.test(d)?(x.name="bracket",x.eos=/[\[{(]/.test(d),"["===d?o.indentation++:"]"===d&&(o.indentation=Math.max(0,o.indentation-1))):t.test(d)?(e.eatWhile(t),x.name="operator",x.eos=";"!==d):/\d/.test(d)?(e.eatWhile(/[\w\d]/),x.name="number"):/[\w_]/.test(d)?(e.eatWhile(/[\w\d_]/),x.name=o.expectVariable?n.test(e.current())?"keyword":"variable":null):x.eos=o.expectVariable,x},s=function(e,t){return e.eatWhile(/[^"]/),new i("comment",e.eat('"')?t.parent:t,!0)},u=function(e,t){return e.eatWhile(/[^']/),new i("string",e.eat("'")?t.parent:t,!1)},c=function(e,t){return e.eatWhile(/[^']/),new i("string-2",e.eat("'")?t.parent:t,!1)},l=function(e,t){var n=new i(null,t,!1),a=e.next();return"|"===a?(n.context=t.parent,n.eos=!0):(e.eatWhile(/[^|]/),n.name="variable"),n};return{startState:function(){return new r},token:function(e,t){if(t.userIndent(e.indentation()),e.eatSpace())return null;var n=t.context.next(e,t.context,t);return t.context=n.context,t.expectVariable=n.eos,n.name},blankLine:function(e){e.userIndent(0)},indent:function(t,n){var a=t.context.next===o&&n&&"]"===n.charAt(0)?-1:t.userIndentationDelta;return(t.indentation+a)*e.indentUnit},electricChars:"]"}})),e.defineMIME("text/x-stsrc",{name:"smalltalk"})}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/147.aed9ee54.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[147],{xhF3:function(n,e,t){(function(n){n(t("VrN/"))})((function(n){"use strict";n.defineMode("solr",(function(){var n=/[^\s\|\!\+\-\*\?\~\^\&\:\(\)\[\]\{\}\"\\]/,e=/[\|\!\+\-\*\?\~\^\&]/,t=/^(OR|AND|NOT|TO)$/i;function r(n){return parseFloat(n).toString()===n}function o(n){return function(e,t){var r,o=!1;while(null!=(r=e.next())){if(r==n&&!o)break;o=!o&&"\\"==r}return o||(t.tokenize=a),"string"}}function i(n){return function(e,t){var r="operator";return"+"==n?r+=" positive":"-"==n?r+=" negative":"|"==n?e.eat(/\|/):"&"==n?e.eat(/\&/):"^"==n&&(r+=" boost"),t.tokenize=a,r}}function u(e){return function(o,i){var u=e;while((e=o.peek())&&null!=e.match(n))u+=o.next();return i.tokenize=a,t.test(u)?"operator":r(u)?"number":":"==o.peek()?"field":"string"}}function a(t,r){var c=t.next();return'"'==c?r.tokenize=o(c):e.test(c)?r.tokenize=i(c):n.test(c)&&(r.tokenize=u(c)),r.tokenize!=a?r.tokenize(t,r):null}return{startState:function(){return{tokenize:a}},token:function(n,e){return n.eatSpace()?null:e.tokenize(n,e)}}})),n.defineMIME("text/x-solr","solr")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/149.b3161487.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[149],{bEWP:function(e,t,a){(function(e){e(a("VrN/"))})((function(e){"use strict";e.defineMode("spreadsheet",(function(){return{startState:function(){return{stringType:null,stack:[]}},token:function(e,t){if(e){switch(0===t.stack.length&&('"'!=e.peek()&&"'"!=e.peek()||(t.stringType=e.peek(),e.next(),t.stack.unshift("string"))),t.stack[0]){case"string":while("string"===t.stack[0]&&!e.eol())e.peek()===t.stringType?(e.next(),t.stack.shift()):"\\"===e.peek()?(e.next(),e.next()):e.match(/^.[^\\\"\']*/);return"string";case"characterClass":while("characterClass"===t.stack[0]&&!e.eol())e.match(/^[^\]\\]+/)||e.match(/^\\./)||t.stack.shift();return"operator"}var a=e.peek();switch(a){case"[":return e.next(),t.stack.unshift("characterClass"),"bracket";case":":return e.next(),"operator";case"\\":return e.match(/\\[a-z]+/)?"string-2":(e.next(),"atom");case".":case",":case";":case"*":case"-":case"+":case"^":case"<":case"/":case"=":return e.next(),"atom";case"$":return e.next(),"builtin"}return e.match(/\d+/)?e.match(/^\w+/)?"error":"number":e.match(/^[a-zA-Z_]\w*/)?e.match(/(?=[\(.])/,!1)?"keyword":"variable-2":-1!=["[","]","(",")","{","}"].indexOf(a)?(e.next(),"bracket"):(e.eatSpace()||e.next(),null)}}}})),e.defineMIME("text/x-spreadsheet","spreadsheet")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/15.772385c8.chunk.css: -------------------------------------------------------------------------------- 1 | .title___23rYW{padding-bottom:20px;font-weight:500;font-size:18px}.space-between___1QXu6{display:flex;align-items:center;justify-content:space-between}.ant3-tabs-bar___1oxKo .ant3-tabs-bar{margin:0;padding:12px 24px} -------------------------------------------------------------------------------- /lightning/static/lightning/157.7ac20074.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[157],{"0sou":function(e,n,t){(function(e){e(t("VrN/"))})((function(e){"use strict";e.defineMode("toml",(function(){return{startState:function(){return{inString:!1,stringType:"",lhs:!0,inArray:0}},token:function(e,n){if(n.inString||'"'!=e.peek()&&"'"!=e.peek()||(n.stringType=e.peek(),e.next(),n.inString=!0),e.sol()&&0===n.inArray&&(n.lhs=!0),n.inString){while(n.inString&&!e.eol())e.peek()===n.stringType?(e.next(),n.inString=!1):"\\"===e.peek()?(e.next(),e.next()):e.match(/^.[^\\\"\']*/);return n.lhs?"property string":"string"}return n.inArray&&"]"===e.peek()?(e.next(),n.inArray--,"bracket"):n.lhs&&"["===e.peek()&&e.skipTo("]")?(e.next(),"]"===e.peek()&&e.next(),"atom"):"#"===e.peek()?(e.skipToEnd(),"comment"):e.eatSpace()?null:n.lhs&&e.eatWhile((function(e){return"="!=e&&" "!=e}))?"property":n.lhs&&"="===e.peek()?(e.next(),n.lhs=!1,null):!n.lhs&&e.match(/^\d\d\d\d[\d\-\:\.T]*Z/)?"atom":n.lhs||!e.match("true")&&!e.match("false")?n.lhs||"["!==e.peek()?!n.lhs&&e.match(/^\-?\d+(?:\.\d+)?/)?"number":(e.eatSpace()||e.next(),null):(n.inArray++,e.next(),"bracket"):"atom"}}})),e.defineMIME("text/x-toml","toml")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/158.ec508940.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[158],{s1o1:function(t,n,e){(function(t){t(e("VrN/"))})((function(t){"use strict";t.defineMode("troff",(function(){var t={};function n(n){if(n.eatSpace())return null;var e=n.sol(),r=n.next();if("\\"===r)return n.match("fB")||n.match("fR")||n.match("fI")||n.match("u")||n.match("d")||n.match("%")||n.match("&")?"string":n.match("m[")?(n.skipTo("]"),n.next(),"string"):n.match("s+")||n.match("s-")?(n.eatWhile(/[\d-]/),"string"):n.match("(")||n.match("*(")?(n.eatWhile(/[\w-]/),"string"):"string";if(e&&("."===r||"'"===r)&&n.eat("\\")&&n.eat('"'))return n.skipToEnd(),"comment";if(e&&"."===r){if(n.match("B ")||n.match("I ")||n.match("R "))return"attribute";if(n.match("TH ")||n.match("SH ")||n.match("SS ")||n.match("HP "))return n.skipToEnd(),"quote";if(n.match(/[A-Z]/)&&n.match(/[A-Z]/)||n.match(/[a-z]/)&&n.match(/[a-z]/))return"attribute"}n.eatWhile(/[\w-]/);var a=n.current();return t.hasOwnProperty(a)?t[a]:null}function e(t,e){return(e.tokens[0]||n)(t,e)}return{startState:function(){return{tokens:[]}},token:function(t,n){return e(t,n)}}})),t.defineMIME("text/troff","troff"),t.defineMIME("text/x-troff","troff"),t.defineMIME("application/x-troff","troff")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/161.b7a28b01.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[161],{P3N9:function(t,n,e){(function(t){t(e("VrN/"))})((function(t){"use strict";t.defineMode("turtle",(function(t){var n,e=t.indentUnit;function o(t){return new RegExp("^(?:"+t.join("|")+")$","i")}o([]);var i=o(["@prefix","@base","a"]),r=/[*+\-<>=&|]/;function c(t,e){var o=t.next();if(n=null,"<"!=o||t.match(/^[\s\u00a0=]/,!1)){if('"'==o||"'"==o)return e.tokenize=u(o),e.tokenize(t,e);if(/[{}\(\),\.;\[\]]/.test(o))return n=o,null;if("#"==o)return t.skipToEnd(),"comment";if(r.test(o))return t.eatWhile(r),null;if(":"==o)return"operator";if(t.eatWhile(/[_\w\d]/),":"==t.peek())return"variable-3";var c=t.current();return i.test(c)?"meta":o>="A"&&o<="Z"?"comment":"keyword"}return t.match(/^[^\s\u00a0>]*>?/),"atom"}function u(t){return function(n,e){var o,i=!1;while(null!=(o=n.next())){if(o==t&&!i){e.tokenize=c;break}i=!i&&"\\"==o}return"string"}}function l(t,n,e){t.context={prev:t.context,indent:t.indent,col:e,type:n}}function a(t){t.indent=t.context.indent,t.context=t.context.prev}return{startState:function(){return{tokenize:c,context:null,indent:0,col:0}},token:function(t,e){if(t.sol()&&(e.context&&null==e.context.align&&(e.context.align=!1),e.indent=t.indentation()),t.eatSpace())return null;var o=e.tokenize(t,e);if("comment"!=o&&e.context&&null==e.context.align&&"pattern"!=e.context.type&&(e.context.align=!0),"("==n)l(e,")",t.column());else if("["==n)l(e,"]",t.column());else if("{"==n)l(e,"}",t.column());else if(/[\]\}\)]/.test(n)){while(e.context&&"pattern"==e.context.type)a(e);e.context&&n==e.context.type&&a(e)}else"."==n&&e.context&&"pattern"==e.context.type?a(e):/atom|string|variable/.test(o)&&e.context&&(/[\}\]]/.test(e.context.type)?l(e,"pattern",t.column()):"pattern"!=e.context.type||e.context.align||(e.context.align=!0,e.context.col=t.column()));return o},indent:function(t,n){var o=n&&n.charAt(0),i=t.context;if(/[\]\}]/.test(o))while(i&&"pattern"==i.type)i=i.prev;var r=i&&o==i.type;return i?"pattern"==i.type?i.col:i.align?i.col+(r?0:1):i.indent+(r?0:e):0},lineComment:"#"}})),t.defineMIME("text/turtle","turtle")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/171.7eeebcb6.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[171],{ztCB:function(t,i,e){(function(t){t(e("VrN/"))})((function(t){"use strict";t.defineMode("yaml",(function(){var t=["true","false","on","off","yes","no"],i=new RegExp("\\b(("+t.join(")|(")+"))$","i");return{token:function(t,e){var n=t.peek(),r=e.escaped;if(e.escaped=!1,"#"==n&&(0==t.pos||/\s/.test(t.string.charAt(t.pos-1))))return t.skipToEnd(),"comment";if(t.match(/^('([^']|\\.)*'?|"([^"]|\\.)*"?)/))return"string";if(e.literal&&t.indentation()>e.keyCol)return t.skipToEnd(),"string";if(e.literal&&(e.literal=!1),t.sol()){if(e.keyCol=0,e.pair=!1,e.pairStart=!1,t.match("---"))return"def";if(t.match("..."))return"def";if(t.match(/\s*-\s+/))return"meta"}if(t.match(/^(\{|\}|\[|\])/))return"{"==n?e.inlinePairs++:"}"==n?e.inlinePairs--:"["==n?e.inlineList++:e.inlineList--,"meta";if(e.inlineList>0&&!r&&","==n)return t.next(),"meta";if(e.inlinePairs>0&&!r&&","==n)return e.keyCol=0,e.pair=!1,e.pairStart=!1,t.next(),"meta";if(e.pairStart){if(t.match(/^\s*(\||\>)\s*/))return e.literal=!0,"meta";if(t.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i))return"variable-2";if(0==e.inlinePairs&&t.match(/^\s*-?[0-9\.\,]+\s?$/))return"number";if(e.inlinePairs>0&&t.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/))return"number";if(t.match(i))return"keyword"}return!e.pair&&t.match(/^\s*(?:[,\[\]{}&*!|>'"%@`][^\s'":]|[^,\[\]{}#&*!|>'"%@`])[^#]*?(?=\s*:($|\s))/)?(e.pair=!0,e.keyCol=t.indentation(),"atom"):e.pair&&t.match(/^:\s*/)?(e.pairStart=!0,"meta"):(e.pairStart=!1,e.escaped="\\"==n,t.next(),null)},startState:function(){return{pair:!1,pairStart:!1,keyCol:0,inlinePairs:0,inlineList:0,literal:!1,escaped:!1}},lineComment:"#",fold:"indent"}})),t.defineMIME("text/x-yaml","yaml"),t.defineMIME("text/yaml","yaml")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/172.4bb01d6a.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[172],{dRHf:function(e,t,r){(function(e){e(r("VrN/"))})((function(e){"use strict";e.defineMode("z80",(function(e,t){var r,n,i=t.ez80;i?(r=/^(exx?|(ld|cp)([di]r?)?|[lp]ea|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|[de]i|halt|im|in([di]mr?|ir?|irx|2r?)|ot(dmr?|[id]rx|imr?)|out(0?|[di]r?|[di]2r?)|tst(io)?|slp)(\.([sl]?i)?[sl])?\b/i,n=/^(((call|j[pr]|rst|ret[in]?)(\.([sl]?i)?[sl])?)|(rs|st)mix)\b/i):(r=/^(exx?|(ld|cp|in)([di]r?)?|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|rst|[de]i|halt|im|ot[di]r|out[di]?)\b/i,n=/^(call|j[pr]|ret[in]?|b_?(call|jump))\b/i);var l=/^(af?|bc?|c|de?|e|hl?|l|i[xy]?|r|sp)\b/i,a=/^(n?[zc]|p[oe]?|m)\b/i,c=/^([hl][xy]|i[xy][hl]|slia|sll)\b/i,s=/^([\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i;return{startState:function(){return{context:0}},token:function(e,t){if(e.column()||(t.context=0),e.eatSpace())return null;var d;if(e.eatWhile(/\w/)){if(i&&e.eat(".")&&e.eatWhile(/\w/),d=e.current(),!e.indentation())return e.match(s)?"number":null;if((1==t.context||4==t.context)&&l.test(d))return t.context=4,"var2";if(2==t.context&&a.test(d))return t.context=4,"var3";if(r.test(d))return t.context=1,"keyword";if(n.test(d))return t.context=2,"keyword";if(4==t.context&&s.test(d))return"number";if(c.test(d))return"error"}else{if(e.eat(";"))return e.skipToEnd(),"comment";if(e.eat('"')){while(d=e.next()){if('"'==d)break;"\\"==d&&e.next()}return"string"}if(e.eat("'")){if(e.match(/\\?.'/))return"number"}else if(e.eat(".")||e.sol()&&e.eat("#")){if(t.context=5,e.eatWhile(/\w/))return"def"}else if(e.eat("$")){if(e.eatWhile(/[\da-f]/i))return"number"}else if(e.eat("%")){if(e.eatWhile(/[01]/))return"number"}else e.next()}return null}}})),e.defineMIME("text/x-z80","z80"),e.defineMIME("text/x-ez80",{name:"z80",ez80:!0})}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/2.20482c57.chunk.css: -------------------------------------------------------------------------------- 1 | :global tr.drop-item-over-downward td{border-bottom:2px dashed #1890ff}:global tr.drop-item-over-upward td{border-top:2px dashed #1890ff}:global .ant3-btn .anticon.anticon-plus>svg{shape-rendering:auto}:global .container{display:block}:global .container .content{display:none}:global .container:hover .content{display:block}.divider___35NWu{position:relative;overflow:hidden;color:#999;font-size:12px}.divider___35NWu:after,.divider___35NWu:before{position:absolute;top:50%;display:inline-block;width:100%;height:1px;background:#ccc;content:""}.divider___35NWu:before{margin-left:-10px;transform:translateX(-100%)}.divider___35NWu:after{margin-left:10px}.pink___11lZ9{color:#eb2f96!important;background:#fff0f6}.magenta___2XI_B{color:#eb2f96!important;background:#fff0f6}.red___38IBt{color:#f5222d!important;background:#fff1f0}.volcano___17Ijo{color:#fa541c!important;background:#fff2e8}.orange___2nOvK{color:#fa8c16!important;background:#fff7e6}.yellow___3oql7{color:#fadb14!important;background:#feffe6}.gold___1jiJt{color:#faad14!important;background:#fffbe6}.cyan___2Ylrb{color:#13c2c2!important;background:#e6fffb}.lime___3ogcY{color:#a0d911!important;background:#fcffe6}.green___1gHs5{color:#52c41a!important;background:#f6ffed}.blue___3DlPi{color:#1890ff!important;background:#e6f7ff}.geekblue___44zYL{color:#2f54eb!important;background:#f0f5ff}.purple___38pTh{color:#722ed1!important;background:#f9f0ff} -------------------------------------------------------------------------------- /lightning/static/lightning/46.bed58943.chunk.css: -------------------------------------------------------------------------------- 1 | .login___FGXBj .ant3-tabs .ant3-tabs-bar{margin-bottom:24px;text-align:center;border-bottom:0}.login___FGXBj .ant3-form-item{margin:0 2px 24px}.login___FGXBj .getCaptcha___1gI6X{display:block;width:100%}.login___FGXBj .icon___EAIwp{margin-left:16px;color:rgba(0,0,0,.2);font-size:24px;vertical-align:middle;cursor:pointer;transition:color .3s}.login___FGXBj .icon___EAIwp:hover{color:#1890ff}.login___FGXBj .other___T3mA5{margin-top:24px;line-height:22px;text-align:left}.login___FGXBj .other___T3mA5 .register___24NQI{float:right}.login___FGXBj .prefixIcon___266NC{color:rgba(0,0,0,.25);font-size:14px}.login___FGXBj .submit___3PlwI{width:100%;margin-top:24px}.main___36hs7{width:388px;margin:0 auto}@media screen and (max-width:576px){.main___36hs7{width:95%}}.main___36hs7 .icon___1JbOs{margin-left:16px;color:rgba(0,0,0,.2);font-size:24px;vertical-align:middle;cursor:pointer;transition:color .3s}.main___36hs7 .icon___1JbOs:hover{color:#1890ff}.main___36hs7 .other___2Akrm{margin-top:24px;line-height:22px;text-align:left}.main___36hs7 .other___2Akrm .register___3P_Y-{float:right} -------------------------------------------------------------------------------- /lightning/static/lightning/47.983aa290.chunk.css: -------------------------------------------------------------------------------- 1 | .exception___2GeG8{display:flex;align-items:center;height:80%;min-height:500px}.exception___2GeG8 .imgBlock___1I7QM{flex:0 0 62.5%;width:62.5%;padding-right:152px;zoom:1}.exception___2GeG8 .imgBlock___1I7QM:after,.exception___2GeG8 .imgBlock___1I7QM:before{display:table;content:" "}.exception___2GeG8 .imgBlock___1I7QM:after{clear:both;height:0;font-size:0;visibility:hidden}.exception___2GeG8 .imgEle___24cxR{float:right;width:100%;max-width:430px;height:360px;background-repeat:no-repeat;background-position:50% 50%;background-size:contain}.exception___2GeG8 .content___kGx-7{flex:auto}.exception___2GeG8 .content___kGx-7 h1{margin-bottom:24px;color:#434e59;font-weight:600;font-size:72px;line-height:72px}.exception___2GeG8 .content___kGx-7 .desc___3GiZp{margin-bottom:16px;color:rgba(0,0,0,.45);font-size:20px;line-height:28px}.exception___2GeG8 .content___kGx-7 .actions___3GpZH button:not(:last-child){margin-right:8px}@media screen and (max-width:1200px){.exception___2GeG8 .imgBlock___1I7QM{padding-right:88px}}@media screen and (max-width:576px){.exception___2GeG8{display:block;text-align:center}.exception___2GeG8 .imgBlock___1I7QM{margin:0 auto 24px;padding-right:0}}@media screen and (max-width:480px){.exception___2GeG8 .imgBlock___1I7QM{margin-bottom:-24px;overflow:hidden}} -------------------------------------------------------------------------------- /lightning/static/lightning/48.983aa290.chunk.css: -------------------------------------------------------------------------------- 1 | .exception___2GeG8{display:flex;align-items:center;height:80%;min-height:500px}.exception___2GeG8 .imgBlock___1I7QM{flex:0 0 62.5%;width:62.5%;padding-right:152px;zoom:1}.exception___2GeG8 .imgBlock___1I7QM:after,.exception___2GeG8 .imgBlock___1I7QM:before{display:table;content:" "}.exception___2GeG8 .imgBlock___1I7QM:after{clear:both;height:0;font-size:0;visibility:hidden}.exception___2GeG8 .imgEle___24cxR{float:right;width:100%;max-width:430px;height:360px;background-repeat:no-repeat;background-position:50% 50%;background-size:contain}.exception___2GeG8 .content___kGx-7{flex:auto}.exception___2GeG8 .content___kGx-7 h1{margin-bottom:24px;color:#434e59;font-weight:600;font-size:72px;line-height:72px}.exception___2GeG8 .content___kGx-7 .desc___3GiZp{margin-bottom:16px;color:rgba(0,0,0,.45);font-size:20px;line-height:28px}.exception___2GeG8 .content___kGx-7 .actions___3GpZH button:not(:last-child){margin-right:8px}@media screen and (max-width:1200px){.exception___2GeG8 .imgBlock___1I7QM{padding-right:88px}}@media screen and (max-width:576px){.exception___2GeG8{display:block;text-align:center}.exception___2GeG8 .imgBlock___1I7QM{margin:0 auto 24px;padding-right:0}}@media screen and (max-width:480px){.exception___2GeG8 .imgBlock___1I7QM{margin-bottom:-24px;overflow:hidden}} -------------------------------------------------------------------------------- /lightning/static/lightning/50.049c1c78.chunk.css: -------------------------------------------------------------------------------- 1 | .mapMarkers___5IVbR{color:#f5222d!important}.map___EBtlj{color:#1890ff!important} -------------------------------------------------------------------------------- /lightning/static/lightning/51.983aa290.chunk.css: -------------------------------------------------------------------------------- 1 | .exception___2GeG8{display:flex;align-items:center;height:80%;min-height:500px}.exception___2GeG8 .imgBlock___1I7QM{flex:0 0 62.5%;width:62.5%;padding-right:152px;zoom:1}.exception___2GeG8 .imgBlock___1I7QM:after,.exception___2GeG8 .imgBlock___1I7QM:before{display:table;content:" "}.exception___2GeG8 .imgBlock___1I7QM:after{clear:both;height:0;font-size:0;visibility:hidden}.exception___2GeG8 .imgEle___24cxR{float:right;width:100%;max-width:430px;height:360px;background-repeat:no-repeat;background-position:50% 50%;background-size:contain}.exception___2GeG8 .content___kGx-7{flex:auto}.exception___2GeG8 .content___kGx-7 h1{margin-bottom:24px;color:#434e59;font-weight:600;font-size:72px;line-height:72px}.exception___2GeG8 .content___kGx-7 .desc___3GiZp{margin-bottom:16px;color:rgba(0,0,0,.45);font-size:20px;line-height:28px}.exception___2GeG8 .content___kGx-7 .actions___3GpZH button:not(:last-child){margin-right:8px}@media screen and (max-width:1200px){.exception___2GeG8 .imgBlock___1I7QM{padding-right:88px}}@media screen and (max-width:576px){.exception___2GeG8{display:block;text-align:center}.exception___2GeG8 .imgBlock___1I7QM{margin:0 auto 24px;padding-right:0}}@media screen and (max-width:480px){.exception___2GeG8 .imgBlock___1I7QM{margin-bottom:-24px;overflow:hidden}} -------------------------------------------------------------------------------- /lightning/static/lightning/52.a2b88eb8.chunk.css: -------------------------------------------------------------------------------- 1 | .main___1LH2Q{width:388px;margin:0 auto}.main___1LH2Q .ant3-form-item{margin-bottom:24px}.main___1LH2Q h3{margin-bottom:20px;font-size:16px}.main___1LH2Q .getCaptcha___1yeL2{display:block;width:100%}.main___1LH2Q .submit___21xow{width:50%}.main___1LH2Q .login___3F_RX{float:right;line-height:40px}.error___1NMIR,.success___Themu,.warning___MmqTw{transition:color .3s}.success___Themu{color:#52c41a}.warning___MmqTw{color:#faad14}.error___1NMIR{color:#f5222d}.progress-pass___3a7Oh>.progress___2iEVg .ant3-progress-bg{background-color:#faad14} -------------------------------------------------------------------------------- /lightning/static/lightning/53.107c795b.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[53],{CVtM:function(e,t,a){"use strict";var n=a("q1tI"),c=a.n(n),l=a("9W6o"),r=a("Z3Gc");t["a"]=c.a.createElement(l["a"],{copyright:r["a"],links:[{key:r["f"],title:r["f"],href:r["g"],blankTarget:!0},{key:r["c"],title:r["c"],href:r["d"],blankTarget:!0}]})},G13B:function(e,t,a){e.exports={container:"container___y_yzr",lang:"lang___HnmO3",content:"content___2YIqa",top:"top___3cVcb",header:"header___jrL0F",logo:"logo___2X07e",title:"title___3SK5R",desc:"desc___2x2oP"}},"Qs/n":function(e,t,a){"use strict";a.r(t),a.d(t,"installPageTitle",(function(){return f})),a.d(t,"installPageSiteDescription",(function(){return u}));var n=a("q1tI"),c=a.n(n),l=a("WHYC"),r=a("uYtH"),i=a("ZFw/"),o=a.n(i),s=a("Z3Gc"),_=a("jfUH"),m=a("CVtM"),d=a("G13B"),g=a.n(d),f="\u95ea\u7535\u6570\u636e\u7ba1\u7406\u7cfb\u7edf\u5b89\u88c5\u5411\u5bfc",u="\u95ea\u7535\u6570\u636e\u7ba1\u7406\uff0c\u9ad8\u54c1\u8d28\u6570\u636e\u7ba1\u7406\u4e13\u5bb6";t["default"]=Object(_["a"])(Object(l["n"])((function(e){var t=e.children,a=e.location.pathname,l=Object(_["gb"])(),i=Object(_["bb"])(),d=i.logo,b=i.title;Object(n["useEffect"])((function(){l({type:"bsmSetting/getSettings"})}),[l]);var p="/user/install"===a;return c.a.createElement(o.a,{title:b||s["f"]},c.a.createElement("div",{className:g.a.container},c.a.createElement("div",{className:g.a.content},c.a.createElement("div",{className:g.a.top},c.a.createElement("div",{className:g.a.header},c.a.createElement(r["a"],{to:"/"},c.a.createElement("img",{alt:"logo",className:g.a.logo,src:d||s["b"]}),c.a.createElement("span",{className:g.a.title},p?f:b||s["f"]))),c.a.createElement("div",{className:g.a.desc},p?u:s["e"])),t),m["a"]))})))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/53.a806c168.chunk.css: -------------------------------------------------------------------------------- 1 | .container___y_yzr{display:flex;flex-direction:column;height:100vh;overflow:auto;background:#f0f2f5}.lang___HnmO3{width:100%;height:40px;line-height:44px;text-align:right}.lang___HnmO3 .ant3-dropdown-trigger{margin-right:24px}.content___2YIqa{flex:1 1;padding:32px 0}@media (min-width:768px){.container___y_yzr{background-image:url(https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg);background-repeat:no-repeat;background-position:center 110px;background-size:100%}.content___2YIqa{padding:32px 0 24px}}.top___3cVcb{text-align:center}.header___jrL0F{height:44px;line-height:44px}.header___jrL0F a{text-decoration:none}.logo___2X07e{height:44px;margin-right:16px;vertical-align:top}.title___3SK5R{position:relative;top:2px;color:rgba(0,0,0,.85);font-weight:600;font-size:33px;font-family:Avenir,"Helvetica Neue",Arial,Helvetica,sans-serif}.desc___2x2oP{margin-top:12px;margin-bottom:40px;color:rgba(0,0,0,.45);font-size:14px} -------------------------------------------------------------------------------- /lightning/static/lightning/6.3db1091f.chunk.css: -------------------------------------------------------------------------------- 1 | .exception___2GeG8{display:flex;align-items:center;height:80%;min-height:500px}.exception___2GeG8 .imgBlock___1I7QM{flex:0 0 62.5%;width:62.5%;padding-right:152px;zoom:1}.exception___2GeG8 .imgBlock___1I7QM:after,.exception___2GeG8 .imgBlock___1I7QM:before{display:table;content:" "}.exception___2GeG8 .imgBlock___1I7QM:after{clear:both;height:0;font-size:0;visibility:hidden}.exception___2GeG8 .imgEle___24cxR{float:right;width:100%;max-width:430px;height:360px;background-repeat:no-repeat;background-position:50% 50%;background-size:contain}.exception___2GeG8 .content___kGx-7{flex:auto}.exception___2GeG8 .content___kGx-7 h1{margin-bottom:24px;color:#434e59;font-weight:600;font-size:72px;line-height:72px}.exception___2GeG8 .content___kGx-7 .desc___3GiZp{margin-bottom:16px;color:rgba(0,0,0,.45);font-size:20px;line-height:28px}.exception___2GeG8 .content___kGx-7 .actions___3GpZH button:not(:last-child){margin-right:8px}@media screen and (max-width:1200px){.exception___2GeG8 .imgBlock___1I7QM{padding-right:88px}}@media screen and (max-width:576px){.exception___2GeG8{display:block;text-align:center}.exception___2GeG8 .imgBlock___1I7QM{margin:0 auto 24px;padding-right:0}}@media screen and (max-width:480px){.exception___2GeG8 .imgBlock___1I7QM{margin-bottom:-24px;overflow:hidden}}.handle____zXwB{position:absolute;top:300px;right:100%;z-index:0;display:flex;align-items:center;justify-content:center;width:48px;height:48px;font-size:16px;text-align:center;background:#1890ff;border-radius:2px 0 0 2px;cursor:pointer;pointer-events:auto} -------------------------------------------------------------------------------- /lightning/static/lightning/63.83003ecb.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[63],{"0+Vp":function(e,t,n){"use strict";n.r(t);var i,a,o=n("k1fw"),r=n("fWQN"),c=n("mtLc"),d=n("yKVA"),l=n("879j"),p=n("q1tI"),s=n.n(p),u=n("/MKj"),h=n("WHYC"),f=n("uYtH"),w=n("s4NR"),g=n.n(w),m=(n("xenO"),{display:"flex",justifyContent:"center"}),b=(i=Object(u["c"])((function(e){var t=e.bsmSetting;return{bsmSetting:t}})),Object(h["n"])(a=i(a=function(e){Object(d["a"])(n,e);var t=Object(l["a"])(n);function n(){return Object(r["a"])(this,n),t.apply(this,arguments)}return Object(c["a"])(n,[{key:"componentDidMount",value:function(){this.init()}},{key:"componentDidUpdate",value:function(){this.init()}},{key:"init",value:function(){var e=this.props.bsmSetting,t=e.third_party_provider,n=(e.wechat_work_appid,e.wechat_work_agentid,g.a.parse(window.location.href.split("?")[1]));n.appid,n.agentid;"wechat"===t&&alert("\u6b64\u529f\u80fd\u6682\u65f6\u5173\u95ed")}},{key:"render",value:function(){var e=this.props.bsmSetting,t=e.wechat_work_able,n=e.dingding_flexible_able,i=void 0===n||n;return s.a.createElement("div",null,t||i?s.a.createElement("div",{id:"qrcode",style:m}):s.a.createElement("div",{style:Object(o["a"])(Object(o["a"])({},m),{},{padding:24})},"\u6b64\u5e94\u7528\u672a\u5f00\u542f\u4f01\u4e1a\u5fae\u4fe1\u767b\u5f55\u529f\u80fd"),s.a.createElement("div",{style:m},s.a.createElement(f["a"],{to:"/user/login"},"\u8d26\u53f7\u5bc6\u7801\u767b\u5f55")))}}]),n}(p["Component"]))||a)||a);t["default"]=b},xenO:function(e,t){!function(e,t){function n(e){var n,i=t.createElement("iframe"),a="https://login.dingtalk.com/login/qrcode.htm?goto="+e.goto;a+=e.style?"&style="+encodeURIComponent(e.style):"",a+=e.href?"&href="+e.href:"",i.src=a,i.frameBorder="0",i.allowTransparency="true",i.scrolling="no",i.width=e.width?e.width+"px":"365px",i.height=e.height?e.height+"px":"400px",n=t.getElementById(e.id),n.innerHTML="",n.appendChild(i)}e.DDLogin=n}(window,document)}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/75.31bb496a.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[75],{VJ2P:function(e,t,a){"use strict";a.r(t);a("Ahcs");var n=a("k0dM"),r=a("0Owb"),o=(a("4X2C"),a("xvon")),l=a("tJVT"),c=a("q1tI"),i=a.n(c),s=a("Hx5s"),u=a("jfUH"),m=a("txBY"),d=a("9uep");function f(e){var t=e.adminConfig,a=e.schemas,s=Object(c["useState"])(""),m=Object(l["a"])(s,2),f=m[0],p=m[1];return i.a.createElement(n["a"],{tabPosition:"left",tabBarStyle:{backgroundColor:"white",paddingTop:16,paddingBottom:16},renderTabBar:function(e,t){return i.a.createElement("div",{style:{display:"flex",flexDirection:"column",float:"left"}},i.a.createElement(o["a"].Search,{placeholder:"\u67e5\u627e\u6a21\u578b...",style:{marginBottom:16},onChange:function(e){return p(e.target.value)}}),i.a.createElement(t,Object(r["a"])({},e,{panels:e.panels.filter((function(e){return!f||e.key.toLowerCase().includes(f.toLowerCase())||e.props.tab.toLowerCase().includes(f.toLowerCase())}))})))}},Object.keys(t).filter(u["cb"]).filter((function(e){return a[e]})).map((function(e){return i.a.createElement(n["a"].TabPane,{tab:a[e].displayName,key:e},i.a.createElement("div",{style:{marginTop:48}},i.a.createElement(d["a"],{modelName:e})))})))}var p=Object(m["a"])((function(e){return i.a.createElement(s["a"],null,i.a.createElement(f,e))}));t["default"]=p}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/76.f8f87284.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[76],{KgJh:function(e,t,o){"use strict";o.r(t);var r=o("q1tI"),n=o.n(r),a=o("WHYC");function i(e){var t=e.match.params.url;return n.a.createElement("div",{style:{left:-24,right:-24,top:-24,bottom:-24,position:"absolute"}},n.a.createElement("iframe",{frameBorder:"0",style:{border:"none",width:"100%",height:"100%"},src:decodeURIComponent(t),title:"autoIframe"}))}t["default"]=Object(a["n"])(i)}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/77.45fb932c.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[77],{Bpwc:function(t,e,n){"use strict";n.r(e);n("smr7");var a,r,i=n("DlTu"),c=n("qLMh"),o=n("9og8"),p=n("fWQN"),s=n("mtLc"),u=n("yKVA"),d=n("879j"),l=n("q1tI"),b=n.n(l),y=n("/MKj"),f=n("WHYC"),g=(a=Object(y["c"])((function(t){var e=t.bsmSetting;return{bsmSetting:e}})),Object(f["n"])(r=a(r=function(t){Object(u["a"])(n,t);var e=Object(d["a"])(n);function n(){var t;Object(p["a"])(this,n);for(var a=arguments.length,r=new Array(a),i=0;i"===o?"atom":"."===o||","===o?"def":void(n.eol()&&(t.commentLine=!1))}}})),n.defineMIME("text/x-brainfuck","brainfuck")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/85.d116d248.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[85],{"qE+Q":function(n,e,t){(function(n){n(t("VrN/"))})((function(n){"use strict";n.defineMode("cmake",(function(){var n=/({)?[a-zA-Z0-9_]+(})?/;function e(n,e){var t,i,c=!1;while(!n.eol()&&(t=n.next())!=e.pending){if("$"===t&&"\\"!=i&&'"'==e.pending){c=!0;break}i=t}return c&&n.backUp(1),t==e.pending?e.continueString=!1:e.continueString=!0,"string"}function t(t,i){var c=t.next();return"$"===c?t.match(n)?"variable-2":"variable":i.continueString?(t.backUp(1),e(t,i)):t.match(/(\s+)?\w+\(/)||t.match(/(\s+)?\w+\ \(/)?(t.backUp(1),"def"):"#"==c?(t.skipToEnd(),"comment"):"'"==c||'"'==c?(i.pending=c,e(t,i)):"("==c||")"==c?"bracket":c.match(/[0-9]/)?"number":(t.eatWhile(/[\w-]/),null)}return{startState:function(){var n={inDefinition:!1,inInclude:!1,continueString:!1,pending:!1};return n},token:function(n,e){return n.eatSpace()?null:t(n,e)}}})),n.defineMIME("text/x-cmake","cmake")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/93.2f7d3fb9.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[93],{"3fnu":function(n,e,i){(function(n){n(i("VrN/"))})((function(n){"use strict";n.defineMode("diff",(function(){var n={"+":"positive","-":"negative","@":"meta"};return{token:function(e){var i=e.string.search(/[\t ]+?$/);if(!e.sol()||0===i)return e.skipToEnd(),("error "+(n[e.string.charAt(0)]||"")).replace(/ $/,"");var t=n[e.peek()]||e.skipToEnd();return-1===i?e.skipToEnd():e.pos=i,t}}})),n.defineMIME("text/x-diff","diff")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/94.13c6d25e.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[94],{"/YIB":function(t,e,n){(function(t){t(n("VrN/"))})((function(t){"use strict";t.defineMode("dtd",(function(t){var e,n=t.indentUnit;function r(t,n){return e=n,t}function a(t,e){var n=t.next();if("<"!=n||!t.eat("!")){if("<"==n&&t.eat("?"))return e.tokenize=c("meta","?>"),r("meta",n);if("#"==n&&t.eatWhile(/[\w]/))return r("atom","tag");if("|"==n)return r("keyword","separator");if(n.match(/[\(\)\[\]\-\.,\+\?>]/))return r(null,n);if(n.match(/[\[\]]/))return r("rule",n);if('"'==n||"'"==n)return e.tokenize=i(n),e.tokenize(t,e);if(t.eatWhile(/[a-zA-Z\?\+\d]/)){var a=t.current();return null!==a.substr(a.length-1,a.length).match(/\?|\+/)&&t.backUp(1),r("tag","tag")}return"%"==n||"*"==n?r("number","number"):(t.eatWhile(/[\w\\\-_%.{,]/),r(null,null))}return t.eatWhile(/[\-]/)?(e.tokenize=u,u(t,e)):t.eatWhile(/[\w]/)?r("keyword","doindent"):void 0}function u(t,e){var n,u=0;while(null!=(n=t.next())){if(u>=2&&">"==n){e.tokenize=a;break}u="-"==n?u+1:0}return r("comment","comment")}function i(t){return function(e,n){var u,i=!1;while(null!=(u=e.next())){if(u==t&&!i){n.tokenize=a;break}i=!i&&"\\"==u}return r("string","tag")}}function c(t,e){return function(n,r){while(!n.eol()){if(n.match(e)){r.tokenize=a;break}n.next()}return t}}return{startState:function(t){return{tokenize:a,baseIndent:t||0,stack:[]}},token:function(t,n){if(t.eatSpace())return null;var r=n.tokenize(t,n),a=n.stack[n.stack.length-1];return"["==t.current()||"doindent"===e||"["==e?n.stack.push("rule"):"endtag"===e?n.stack[n.stack.length-1]="endtag":"]"==t.current()||"]"==e||">"==e&&"rule"==a?n.stack.pop():"["==e&&n.stack.push("["),r},indent:function(t,r){var a=t.stack.length;return"]"===r.charAt(0)?a--:">"===r.substr(r.length-1,r.length)&&("<"===r.substr(0,1)||"doindent"==e&&r.length>1||("doindent"==e?a--:">"==e&&r.length>1||"tag"==e&&">"!==r||("tag"==e&&"rule"==t.stack[t.stack.length-1]?a--:"tag"==e?a++:">"===r&&"rule"==t.stack[t.stack.length-1]&&">"===e?a--:">"===r&&"rule"==t.stack[t.stack.length-1]||("<"!==r.substr(0,1)&&">"===r.substr(0,1)?a-=1:">"===r||(a-=1)))),null!=e&&"]"!=e||a--),t.baseIndent+a*n},electricChars:"]>"}})),t.defineMIME("application/xml-dtd","dtd")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/98.9121f030.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[98],{t86p:function(e,t,n){(function(e){e(n("VrN/"))})((function(e){"use strict";e.defineMode("eiffel",(function(){function e(e){for(var t={},n=0,r=e.length;n>"]);function r(e,t,n){return n.tokenize.push(e),e(t,n)}function i(e,t){if(e.eatSpace())return null;var n=e.next();return'"'==n||"'"==n?r(a(n,"string"),e,t):"-"==n&&e.eat("-")?(e.skipToEnd(),"comment"):":"==n&&e.eat("=")?"operator":/[0-9]/.test(n)?(e.eatWhile(/[xXbBCc0-9\.]/),e.eat(/[\?\!]/),"ident"):/[a-zA-Z_0-9]/.test(n)?(e.eatWhile(/[a-zA-Z_0-9]/),e.eat(/[\?\!]/),"ident"):/[=+\-\/*^%<>~]/.test(n)?(e.eatWhile(/[=+\-\/*^%<>~]/),"operator"):null}function a(e,t,n){return function(r,i){var a,o=!1;while(null!=(a=r.next())){if(a==e&&(n||!o)){i.tokenize.pop();break}o=!o&&"%"==a}return t}}return{startState:function(){return{tokenize:[i]}},token:function(e,r){var i=r.tokenize[r.tokenize.length-1](e,r);if("ident"==i){var a=e.current();i=t.propertyIsEnumerable(e.current())?"keyword":n.propertyIsEnumerable(e.current())?"operator":/^[A-Z][A-Z_0-9]*$/g.test(a)?"tag":/^0[bB][0-1]+$/g.test(a)||/^0[cC][0-7]+$/g.test(a)||/^0[xX][a-fA-F0-9]+$/g.test(a)||/^([0-9]+\.[0-9]*)|([0-9]*\.[0-9]+)$/g.test(a)||/^[0-9]+$/g.test(a)?"number":"variable"}return i},lineComment:"--"}})),e.defineMIME("text/x-eiffel","eiffel")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/99.6f76ffe3.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[99],{Rba3:function(e,t,n){(function(e){e(n("VrN/"))})((function(e){"use strict";e.defineMode("elm",(function(){function e(e,t,n){return t(n),n(e,t)}var t=/[a-z]/,n=/[A-Z]/,r=/[a-zA-Z0-9_]/,i=/[0-9]/,o=/[0-9A-Fa-f]/,u=/[-&*+.\\/<>=?^|:]/,a=/[(),[\]{}]/,f=/[ \v\f]/;function s(){return function(s,w){if(s.eatWhile(f))return null;var x=s.next();if(a.test(x))return"{"===x&&s.eat("-")?e(s,w,l(1)):"["===x&&s.match("glsl|")?e(s,w,m):"builtin";if("'"===x)return e(s,w,h);if('"'===x)return s.eat('"')?s.eat('"')?e(s,w,c):"string":e(s,w,p);if(n.test(x))return s.eatWhile(r),"variable-2";if(t.test(x)){var d=1===s.pos;return s.eatWhile(r),d?"def":"variable"}if(i.test(x)){if("0"===x){if(s.eat(/[xX]/))return s.eatWhile(o),"number"}else s.eatWhile(i);return s.eat(".")&&s.eatWhile(i),s.eat(/[eE]/)&&(s.eat(/[-+]/),s.eatWhile(i)),"number"}return u.test(x)?"-"===x&&s.eat("-")?(s.skipToEnd(),"comment"):(s.eatWhile(u),"keyword"):"_"===x?"keyword":"error"}}function l(e){return 0==e?s():function(t,n){while(!t.eol()){var r=t.next();if("{"==r&&t.eat("-"))++e;else if("-"==r&&t.eat("}")&&(--e,0===e))return n(s()),"comment"}return n(l(e)),"comment"}}function c(e,t){while(!e.eol()){var n=e.next();if('"'===n&&e.eat('"')&&e.eat('"'))return t(s()),"string"}return"string"}function p(e,t){while(e.skipTo('\\"'))e.next(),e.next();return e.skipTo('"')?(e.next(),t(s()),"string"):(e.skipToEnd(),t(s()),"error")}function h(e,t){while(e.skipTo("\\'"))e.next(),e.next();return e.skipTo("'")?(e.next(),t(s()),"string"):(e.skipToEnd(),t(s()),"error")}function m(e,t){while(!e.eol()){var n=e.next();if("|"===n&&e.eat("]"))return t(s()),"string"}return"string"}var w={case:1,of:1,as:1,if:1,then:1,else:1,let:1,in:1,type:1,alias:1,module:1,where:1,import:1,exposing:1,port:1};return{startState:function(){return{f:s()}},copyState:function(e){return{f:e.f}},lineComment:"--",token:function(e,t){var n=t.f(e,(function(e){t.f=e})),r=e.current();return w.hasOwnProperty(r)?"keyword":n}}})),e.defineMIME("text/x-elm","elm")}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/CNAME: -------------------------------------------------------------------------------- 1 | preview.pro.ant.design -------------------------------------------------------------------------------- /lightning/static/lightning/admin-panel.614324f6.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[21],{Y3Wu:function(n,d,i){"use strict";i.r(d);var o=i("yPhJ");i.d(d,"default",(function(){return o["AdminEditorCard"]}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/amap.72a3a5ac.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[22],{CCXB:function(e,n,r){"use strict";r.r(n);var i=r("PpiC"),t=r("q1tI"),a=r.n(t),c=r("7DBZ"),o=r("GGQb"),p=r("Nz1C"),s=r.n(p),u=["children"];n["default"]=function(e){var n=e.children,r=Object(i["a"])(e,u);return a.a.createElement(c["Map"],r,n({Wrapper:o["Wrapper"],PositionPicker:o["PositionPicker"],Autocomplete:s.a}))}}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/code-editor.9bf73167.chunk.css: -------------------------------------------------------------------------------- 1 | .codeEditor___2c7ZQ .CodeMirror-fullscreen{position:fixed;top:0;right:0;bottom:0;left:0;z-index:99;height:auto}body,html{overflow-y:auto!important} -------------------------------------------------------------------------------- /lightning/static/lightning/dnd.1c9d98ce.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[24],{ZnhR:function(a,n,e){"use strict";e.r(n);var r=e("q1tI"),t=e.n(r),c=e("kvAW"),o=e("jkD3"),u=e("jA2l"),i=e("qJlv");n["default"]=function(a){var n=a.children;return t.a.createElement(c["a"],{backend:i["a"]},n({DragSource:o["a"],DropTarget:u["a"]}))}}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/excel.d8343a7d.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[25],{21:function(n,i){},22:function(n,i){},3:function(n,i){},vSpB:function(n,i,o){"use strict";o.r(i);var t=o("EUZL");o.d(i,"XLSX",(function(){return t}));var u=o("2Vgw"),c=o.n(u);o.d(i,"pinyin",(function(){return c.a}))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/favicon.png -------------------------------------------------------------------------------- /lightning/static/lightning/home_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/home_bg.png -------------------------------------------------------------------------------- /lightning/static/lightning/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/icons/icon-128x128.png -------------------------------------------------------------------------------- /lightning/static/lightning/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/icons/icon-192x192.png -------------------------------------------------------------------------------- /lightning/static/lightning/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/icons/icon-512x512.png -------------------------------------------------------------------------------- /lightning/static/lightning/img.08952397.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[26],{U2hj:function(n,t,e){"use strict";e.r(t);var o=e("q1tI"),a=e.n(o),u=e("tZYZ"),i=e("jfUH");function l(n){var t=n.value,e=n.isExpand,o=n.uploadProvider,l=n.urlImgName,r=Object(u["c"])(),c=r.openLightbox,s=t.map((function(n){var t,e;return{src:null!==(t=null===n||void 0===n?void 0:n[l])&&void 0!==t?t:n,thumbnail:Object(i["x"])(null!==(e=null===n||void 0===n?void 0:n[l])&&void 0!==e?e:n,{width:100,height:80,uploadProvider:o})}}));return a.a.createElement(a.a.Fragment,null,s.map((function(n){return n.thumbnail})).slice(0,3).map((function(n,t){return a.a.createElement("img",{src:n,style:{height:e?100:50,cursor:"zoom-in"},alt:" ",onClick:function(){return c(t)}})})),a.a.createElement(u["a"],{key:s.length,elements:s,options:{buttons:{showThumbnailsButton:!1,showAutoplayButton:!1},settings:{disablePanzoom:!0}}}))}t["default"]=function(n){return a.a.createElement(u["b"],null,a.a.createElement(l,n))}}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/installing.d03c439b.chunk.css: -------------------------------------------------------------------------------- 1 | .t1___9DtNp{color:rgba(0,0,0,.45);font-size:14px}.t2___2f3v_{margin-top:16px;color:rgba(0,0,0,.45)}.t3___2QEZM{margin:48px 0 16px;color:#8c8c8c;font-weight:700;font-size:16px}.success___gC_pU{display:flex;flex-direction:column;align-items:center}.install-form .ant3-form-item-label{width:inherit!important}.install-form .ant3-form-item-label>label:not(.ant3-form-item-required):before{margin-right:4px;opacity:0;content:"*"} -------------------------------------------------------------------------------- /lightning/static/lightning/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/logo.png -------------------------------------------------------------------------------- /lightning/static/lightning/p__BSMApp.a9c02d11.chunk.css: -------------------------------------------------------------------------------- 1 | .success___1ltB-{border-color:#52c41a!important}.error___bK3jg{border-color:#f5222d!important}.container___3pl9V{display:flex}.container___3pl9V .content___3e9Ph{width:66px;background:#fff}.container___3pl9V .content___3e9Ph .content_box___Uidox{display:inline-block;margin-left:8px;visibility:hidden}.container___3pl9V:hover .content___3e9Ph .content_box___Uidox{visibility:visible}.select___3x4xw .ant3-tree li .ant3-tree-node-content-wrapper{padding-right:0!important}.descriptionList___3sgFN .ant3-row{margin-bottom:-16px;overflow:hidden}.descriptionList___3sgFN+.descriptionList___3sgFN .ant3-row{margin-top:16px}.descriptionList___3sgFN .title___jo_Je{margin-bottom:16px;color:rgba(0,0,0,.85);font-weight:500;font-size:14px}.descriptionList___3sgFN .term___O9ehC{display:table-cell;padding-bottom:16px;color:rgba(0,0,0,.85);line-height:20px;white-space:nowrap}.descriptionList___3sgFN .term___O9ehC:after{position:relative;top:-.5px;margin:0 8px 0 2px;content:":"}.descriptionList___3sgFN .detail___2YYjY{display:table-cell;width:100%;padding-bottom:16px;color:rgba(0,0,0,.85);line-height:20px}.descriptionList___3sgFN.small___17xaW .ant3-row{margin-bottom:-8px}.descriptionList___3sgFN.small___17xaW+.descriptionList___3sgFN .ant3-row{margin-top:8px}.descriptionList___3sgFN.small___17xaW .title___jo_Je{margin-bottom:12px;color:rgba(0,0,0,.85)}.descriptionList___3sgFN.small___17xaW .detail___2YYjY,.descriptionList___3sgFN.small___17xaW .term___O9ehC{padding-bottom:8px}.descriptionList___3sgFN.large___1SsxZ .title___jo_Je{font-size:16px}.descriptionList___3sgFN.vertical___2V3oj .term___O9ehC{display:block;padding-bottom:8px}.descriptionList___3sgFN.vertical___2V3oj .detail___2YYjY{display:block} -------------------------------------------------------------------------------- /lightning/static/lightning/react-doc-viewer.adb5949f.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[28],{16:function(e,n){},17:function(e,n){},18:function(e,n){},19:function(e,n){},20:function(e,n){},"YUX/":function(e,n,t){"use strict";t.r(n),function(e){var i=t("tJVT"),o=(t("2qtc"),t("kLXV")),c=t("4Qd8"),l=t.n(c),a=t("q1tI"),r=t.n(a),s=o["a"].confirm,u=["bmp","doc","docx","htm","html","jpg","jpeg","pdf","png","ppt","pptx","tiff","txt","xls","xlsx"],d=function(n){var t=n.url,d=void 0===t?"":t,f=n.title,p=n.children,h=Object(a["useState"])(!1),w=Object(i["a"])(h,2),b=w[0],m=w[1],g=function(){window.location.href=d},v=function(){m(!1)},x=d.lastIndexOf("."),O=function(){var e=d.substr(x+1).split("#")[0];u.some((function(n){return n===e}))?m(!0):s({title:"\u63d0\u793a",content:"\u662f\u5426\u4e0b\u8f7d\u8be5\u6587\u4ef6",onOk:function(){g()}})},k=null!==f&&void 0!==f?f:d.substring(d.lastIndexOf("/")+1,x),j=Object(a["useMemo"])((function(){return r.a.createElement(l.a,{config:{header:{disableHeader:!0}},style:{width:"100%",height:"100%"},pluginRenderers:c["DocViewerRenderers"],documents:[{uri:d}]})}),[d]);return r.a.createElement("div",{className:"file-view"},r.a.createElement(o["a"],{className:"fileModal",title:k,bodyStyle:{background:"green",height:"80vh",padding:"0"},width:"80vw",okText:"\u4e0b\u8f7d",cancelText:"\u5173\u95ed",onOk:g,onCancel:v,visible:b,destroyOnClose:!0},j),e.isFunction(p)?p({onShow:O}):p)};n["default"]=d}.call(this,t("LvDl"))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/rich-text.3e45f05d.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[29],{NKAz:function(t,e,a){"use strict";a.r(e),function(t){var r=a("0Owb"),n=a("k1fw"),o=a("qLMh"),i=a("9og8"),c=a("fWQN"),s=a("mtLc"),u=a("yKVA"),l=a("879j"),d=a("q1tI"),p=a.n(d),v=a("yEr3"),h=a.n(v),f=(a("Lzxq"),a("mmaM")),b=a("AAgL"),m=(a("L/94"),function(e){Object(u["a"])(d,e);var a=Object(l["a"])(d);function d(){var e;Object(c["a"])(this,d);for(var r=arguments.length,n=new Array(r),s=0;s

"===i||e.setState({editorState:t},(function(){return r(i)}))}}),200),e.upload=function(){var t=Object(i["a"])(Object(o["a"])().mark((function t(e){var a,r,n,i,c;return Object(o["a"])().wrap((function(t){while(1)switch(t.prev=t.next){case 0:return a=e.file,r=e.success,n=e.error,t.prev=1,t.next=4,Object(f["uploadServer"])(a);case 4:i=t.sent,c=i.url,r({url:c,meta:{id:c,title:a.name,alt:a.name,loop:!1,autoPlay:!1,controls:!1}}),t.next=13;break;case 9:t.prev=9,t.t0=t["catch"](1),console.trace(t.t0),n({msg:"\u4e0a\u4f20\u5931\u8d25\uff0c\u8bf7\u91cd\u65b0\u4e0a\u4f20"});case 13:case"end":return t.stop()}}),t,null,[[1,9]])})));return function(e){return t.apply(this,arguments)}}(),e}return Object(s["a"])(d,[{key:"componentWillReceiveProps",value:function(t){var e=t.value,a=this.state.editorState,r=a&&a.toHTML();e!==r&&this.setState({editorState:h.a.createEditorState(e)})}},{key:"render",value:function(){var t=this.props,e=t.LONGTEXT,a=t.value;if(e)return p.a.createElement(h.a,{value:h.a.createEditorState(a),style:{resize:"none",border:"1px solid #d9d9d9",borderRadius:4},readOnly:!0,controls:[]});var o=this.state.editorState;return p.a.createElement(h.a,Object(r["a"])({onChange:this.onChange,value:o,className:"ant-input"},this.widgetProps,{media:Object(n["a"])({uploadFn:this.upload},this.widgetProps.media)}))}}]),d}(b["a"]));e["default"]=m}.call(this,a("LvDl"))}}]); -------------------------------------------------------------------------------- /lightning/static/lightning/static/BarChart.44321352.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/BarChart.44321352.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/CharTabel.bf487762.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/CharTabel.bf487762.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/Column-lineComboChart.089ebe73.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/Column-lineComboChart.089ebe73.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/CurveStatisticsCard.368c916f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/CurveStatisticsCard.368c916f.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/Funnelchart.a45b9353.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/Funnelchart.a45b9353.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/LineChart.09df74ac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/LineChart.09df74ac.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/PieChart .6520be22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/PieChart .6520be22.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/RadarChart.e91c612c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/RadarChart.e91c612c.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/logo-enterprise.44e6a2eb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/logo-enterprise.44e6a2eb.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/logo.3ac90302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/logo.3ac90302.png -------------------------------------------------------------------------------- /lightning/static/lightning/static/panda.58d815cb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/panda.58d815cb.jpg -------------------------------------------------------------------------------- /lightning/static/lightning/static/table.02a8a35d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/static/table.02a8a35d.png -------------------------------------------------------------------------------- /lightning/static/lightning/vendors~amap.db0a0295.chunk.css: -------------------------------------------------------------------------------- 1 | .circleMar{height:50px;width:50px;border-radius:50%;background-color:red;position:relative;transition:height .25s ease,width .25s ease;transform:translate(-50%,-50%)}.circleMar:after,.circleMar:before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border-radius:50%;border:1px solid red}.circleMar:before{animation:ripple 2s linear infinite}.circleMar:after{animation:ripple 2s linear 1s infinite}@keyframes ripple{0%{transform:scale(1)}75%{transform:scale(1.75);opacity:1}to{transform:scale(2);opacity:0}}.infoWin{border:1px solid silver;background:url(static/panda.58d815cb.jpg) no-repeat 50%;background-size:100% 100%;color:transparent}div.infoWin-top{position:relative;background:none repeat scroll 0 0 transparent;border-bottom:1px solid #ccc;border-radius:5px 5px 0 0}div.infoWin-top div{display:inline-block;color:#333;font-size:14px;font-weight:700;line-height:31px;padding:0 10px}div.infoWin-top img{position:absolute;top:10px;right:10px;transition-duration:.25s}div.infoWin-top img:hover{box-shadow:0 0 5px #000}div.infoWin-middle{font-size:12px;padding:6px;line-height:20px}div.infoWin-bottom{height:0;width:100%;clear:both;text-align:center}div.infoWin-bottom img{position:relative;z-index:104}.infoWin-middle img{float:left;margin-right:6px} -------------------------------------------------------------------------------- /lightning/static/lightning/video.92647ec0.chunk.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/lightning/static/lightning/video.92647ec0.chunk.css -------------------------------------------------------------------------------- /lightning/static/lightning/video.ef77bd9a.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[41],{"+SAl":function(e,t,n){},"7IKC":function(e,t,n){"use strict";n.r(t);n("52nx");var a=n("7bDm"),i=(n("eE3J"),n("GSZB")),l=n("fWQN"),s=n("mtLc"),c=n("yKVA"),o=n("879j"),r=n("q1tI"),u=n.n(r),h=n("udzn"),d=n("AAgL"),f=(n("+SAl"),function(e){Object(c["a"])(n,e);var t=Object(o["a"])(n);function n(){var e;Object(l["a"])(this,n);for(var a=arguments.length,i=new Array(a),s=0;s source.rght: 16 | position = 'right' 17 | source.move_to(target, position=position) 18 | -------------------------------------------------------------------------------- /puzzle/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from puzzle.models import Block 3 | from puzzle.services import move 4 | 5 | 6 | class BlockMoveTest(TestCase): 7 | def setUp(self): 8 | self.root = Block.objects.create() 9 | self.child1 = self.root.children.create() 10 | self.child2 = self.root.children.create() 11 | self.root2 = Block.objects.create() 12 | 13 | def assertChildren(self, parent, children_id): 14 | self.assertListEqual(children_id, list(parent.children.values_list('id', flat=True))) 15 | 16 | def test_to_right(self): 17 | move(self.child1.id, self.root.id, 1) 18 | self.assertChildren(self.root, [self.child2.id, self.child1.id]) 19 | 20 | def test_to_left(self): 21 | move(self.child2.id, self.root.id, 0) 22 | self.assertChildren(self.root, [self.child2.id, self.child1.id]) 23 | 24 | def test_move(self): 25 | move(self.child1.id, self.root2.id, 0) 26 | self.assertChildren(self.root, [self.child2.id]) 27 | self.assertChildren(self.root2, [self.child1.id]) 28 | move(self.child2.id, self.root2.id, 0) 29 | self.assertChildren(self.root, []) 30 | self.assertChildren(self.root2, [self.child2.id, self.child1.id]) 31 | move(self.child2.id, self.root.id, 0) 32 | self.assertChildren(self.root, [self.child2.id]) 33 | self.assertChildren(self.root2, [self.child1.id]) 34 | move(self.child1.id, self.root.id, 1) 35 | self.assertChildren(self.root, [self.child2.id, self.child1.id]) 36 | self.assertChildren(self.root2, []) 37 | -------------------------------------------------------------------------------- /puzzle/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | urlpatterns = [ 5 | path('', views.block_view), 6 | path('/move', views.move), 7 | ] 8 | -------------------------------------------------------------------------------- /puzzle/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.decorators import api_view 2 | from api_basebone.drf.response import success_response 3 | from . import component_resolver_map, services 4 | from .models import Block 5 | 6 | 7 | @api_view() 8 | def block_view(request, block_id): 9 | block = Block.objects.get(id=block_id) 10 | data = None 11 | if block.component in component_resolver_map: 12 | data = component_resolver_map[block.component](block) 13 | return success_response(data) 14 | 15 | 16 | @api_view(['PUT']) 17 | def move(request, block_id): 18 | parent = request.data.get('parent', None) 19 | index = request.data['index'] 20 | services.move(block_id, parent, index) 21 | return success_response() 22 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/setup.cfg -------------------------------------------------------------------------------- /shield/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'shield.apps.ShieldConfig' 2 | -------------------------------------------------------------------------------- /shield/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ShieldConfig(AppConfig): 5 | name = 'shield' 6 | 7 | def ready(self): 8 | from . import filter, signals 9 | -------------------------------------------------------------------------------- /shield/bsm/admin.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from api_basebone.core.admin import register, BSMAdmin 3 | from .. import models 4 | 5 | 6 | @register 7 | class RuleAdmin(BSMAdmin): 8 | # filter = ['model', 'groups'] 9 | display = ['model', 'groups'] 10 | form_fields = [ 11 | 'model' if hasattr(settings, 'SHIELD_MODEL') else {'name': 'model', 'widget': 'ModelSelect'}, 12 | 'groups', 13 | {'name': 'condition', 'params': {'canAdd': True, 'fields': [ 14 | 'field', {'name': 'operator', 'disabled': 'true'}, {'name': 'variable', 'disabled': 'true'} 15 | ]}}, 16 | {'name': 'combinator', 'widget': 'Radio'}, 17 | ] 18 | inline_actions = ['edit', 'delete'] 19 | 20 | class Meta: 21 | model = models.Rule 22 | -------------------------------------------------------------------------------- /shield/migrations/0002_rule_combinator.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-09-02 01:58 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('shield', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='rule', 15 | name='combinator', 16 | field=models.CharField(choices=[('&', '与'), ('|', '或')], default='&', max_length=1, verbose_name='组合方式'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /shield/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/shield/migrations/__init__.py -------------------------------------------------------------------------------- /shield/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | from django.contrib.auth.models import Group 4 | 5 | from api_basebone.utils.operators import OPERATOR_MAP 6 | 7 | 8 | class Rule(models.Model): 9 | COMBINATOR_AND = '&' 10 | COMBINATOR_OR = '|' 11 | 12 | model = models.ForeignKey(settings.SHIELD_MODEL, on_delete=models.CASCADE, verbose_name='关联模型')\ 13 | if hasattr(settings, 'SHIELD_MODEL') else models.CharField('关联模型', max_length=191) 14 | groups = models.ManyToManyField(Group, verbose_name='关联用户组', help_text='不选会应用于全部', blank=True) 15 | combinator = models.CharField('组合方式', choices=((COMBINATOR_AND, '与'), (COMBINATOR_OR, '或')), max_length=1, default=COMBINATOR_AND) 16 | 17 | def get_rule(self): 18 | conditions = [c.to_dict() for c in self.condition_set.all()] 19 | return { 20 | 'conditions': conditions if self.combinator == self.COMBINATOR_AND else [{'operator': 'OR', 'children': conditions}], 21 | 'groups': {r.name for r in self.groups.all()}, 22 | } 23 | 24 | class Meta: 25 | verbose_name = '规则' 26 | verbose_name_plural = verbose_name 27 | 28 | 29 | class Condition(models.Model): 30 | rule = models.ForeignKey(Rule, on_delete=models.CASCADE, verbose_name='所属规则') 31 | field = models.CharField('字段', max_length=100) 32 | operator = models.CharField('操作符', choices=[(o, o) for o in OPERATOR_MAP.keys()], max_length=20, default='=') 33 | variable = models.CharField('变量', choices=[['user', '用户']], max_length=50, default='user') 34 | 35 | def to_dict(self): 36 | return {'field': self.field.replace('.', '__'), 'operator': self.operator, 'expression': self.variable} 37 | 38 | class Meta: 39 | verbose_name = '过滤条件' 40 | verbose_name_plural = verbose_name 41 | -------------------------------------------------------------------------------- /storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-men/lightning/c817de6df5f05e431563b1b8ec1533e0f23852ad/storage/__init__.py -------------------------------------------------------------------------------- /storage/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | from . import views 3 | 4 | urlpatterns = [ 5 | path('upload', views.upload), 6 | re_path(r'file/+(?P.*)', views.file), 7 | ] 8 | -------------------------------------------------------------------------------- /storage/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from django.http import FileResponse 4 | from rest_framework.decorators import api_view, parser_classes 5 | from rest_framework.parsers import MultiPartParser 6 | 7 | from api_basebone.core.exceptions import BusinessException 8 | from api_basebone.drf.response import success_response 9 | from bsm_config.settings import site_setting 10 | 11 | 12 | def is_relative_to(path, dir_path): 13 | # 避免 path traversal 问题 https://owasp.org/www-community/attacks/Path_Traversal 14 | # Python 3.9 才有 Path.is_relative_to, 15 | # 而 Path.resolve 会 follow symbol link, 16 | # Path.absolute 又不能解析“../”, 17 | # 所以只能用 os.path.abspath 了 18 | return os.path.abspath(path).startswith(os.path.abspath(dir_path)) 19 | 20 | 21 | @api_view(['POST']) 22 | @parser_classes([MultiPartParser]) 23 | def upload(request): 24 | key, policy, file = request.data['key'], request.data['policy'], request.data['file'] 25 | storage_path = site_setting['storage_path'] 26 | if not storage_path: 27 | raise BusinessException('storage support not enabled') 28 | file_path = Path(storage_path).joinpath(key) 29 | if not is_relative_to(file_path, storage_path): 30 | raise BusinessException('invalid file key: %s' % key) 31 | dirname = file_path.parent 32 | if not dirname.exists(): 33 | dirname.mkdir(parents=True) 34 | elif not dirname.is_dir(): 35 | raise BusinessException('dir exists: %s' % os.path.dirname(key)) 36 | elif file_path.exists(): 37 | raise BusinessException('file already exists: %s' % key) 38 | with file_path.open('wb+') as f: 39 | for chunk in file.chunks(): 40 | f.write(chunk) 41 | return success_response() 42 | 43 | 44 | def file(request, key): 45 | storage_path = site_setting['storage_path'] 46 | file_path = Path(storage_path).joinpath(key) 47 | if not is_relative_to(file_path, storage_path): 48 | raise BusinessException('invalid file key: %s' % key) 49 | if not file_path.is_file(): 50 | raise BusinessException('file not exists: %s' % key) 51 | return FileResponse(file_path.open('rb')) 52 | --------------------------------------------------------------------------------