├── .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 |
--------------------------------------------------------------------------------