├── data └── .gitkeep ├── pekja ├── __init__.py ├── asgi.py ├── wsgi.py ├── urls.py ├── settings.py └── utils.py ├── layuimini ├── __init__.py ├── migrations │ └── __init__.py ├── models.py ├── admin.py ├── tests.py ├── apps.py └── urls.py ├── command ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── is_tool_empty.py │ │ ├── parse.py │ │ ├── cron_all_task.py │ │ ├── update_input.py │ │ ├── record_report.py │ │ ├── set_record_report_cron.py │ │ └── init_admin.py ├── __init__.py ├── apps.py ├── report.py └── cron_task.py ├── entities ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── __init__.py ├── apps.py ├── forms.py ├── validators.py ├── widgets.py ├── admin.py ├── resources.py └── models.py ├── parse ├── examples │ ├── sublist3r.txt │ ├── ctfr.txt │ ├── lijiejie_sub_domains_brute.txt │ ├── one_for_all.json │ ├── nmap_sub_domain_brute.xml │ └── censys_enumeration.json ├── ctfr.py ├── sublist3r_parser.py ├── nmap_dns_brute.py ├── one_for_all.py ├── lijiejie_subdomains_brute.py ├── censys_enumeration_email.py ├── censys_enumeration_domain.py ├── nmap_syn_scan.py ├── nmap_udp_scan.py ├── nmap_http_scan.py ├── __init__.py ├── parser.py └── tests.py ├── docker ├── env.example ├── sources.list ├── update.sh ├── start.sh └── tool.json ├── screenshot ├── pekja-web.png └── pekja-report.jpg ├── requirements.txt ├── static └── layuimini │ ├── images │ ├── bg.jpg │ ├── logo.png │ └── favicon.ico │ ├── lib │ ├── layui-v2.5.5 │ │ ├── font │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ └── iconfont.woff2 │ │ ├── images │ │ │ └── face │ │ │ │ ├── 0.gif │ │ │ │ ├── 1.gif │ │ │ │ ├── 2.gif │ │ │ │ ├── 3.gif │ │ │ │ ├── 4.gif │ │ │ │ ├── 5.gif │ │ │ │ ├── 6.gif │ │ │ │ ├── 7.gif │ │ │ │ ├── 8.gif │ │ │ │ ├── 9.gif │ │ │ │ ├── 10.gif │ │ │ │ ├── 11.gif │ │ │ │ ├── 12.gif │ │ │ │ ├── 13.gif │ │ │ │ ├── 14.gif │ │ │ │ ├── 15.gif │ │ │ │ ├── 16.gif │ │ │ │ ├── 17.gif │ │ │ │ ├── 18.gif │ │ │ │ ├── 19.gif │ │ │ │ ├── 20.gif │ │ │ │ ├── 21.gif │ │ │ │ ├── 22.gif │ │ │ │ ├── 23.gif │ │ │ │ ├── 24.gif │ │ │ │ ├── 25.gif │ │ │ │ ├── 26.gif │ │ │ │ ├── 27.gif │ │ │ │ ├── 28.gif │ │ │ │ ├── 29.gif │ │ │ │ ├── 30.gif │ │ │ │ ├── 31.gif │ │ │ │ ├── 32.gif │ │ │ │ ├── 33.gif │ │ │ │ ├── 34.gif │ │ │ │ ├── 35.gif │ │ │ │ ├── 36.gif │ │ │ │ ├── 37.gif │ │ │ │ ├── 38.gif │ │ │ │ ├── 39.gif │ │ │ │ ├── 40.gif │ │ │ │ ├── 41.gif │ │ │ │ ├── 42.gif │ │ │ │ ├── 43.gif │ │ │ │ ├── 44.gif │ │ │ │ ├── 45.gif │ │ │ │ ├── 46.gif │ │ │ │ ├── 47.gif │ │ │ │ ├── 48.gif │ │ │ │ ├── 49.gif │ │ │ │ ├── 50.gif │ │ │ │ ├── 51.gif │ │ │ │ ├── 52.gif │ │ │ │ ├── 53.gif │ │ │ │ ├── 54.gif │ │ │ │ ├── 55.gif │ │ │ │ ├── 56.gif │ │ │ │ ├── 57.gif │ │ │ │ ├── 58.gif │ │ │ │ ├── 59.gif │ │ │ │ ├── 60.gif │ │ │ │ ├── 61.gif │ │ │ │ ├── 62.gif │ │ │ │ ├── 63.gif │ │ │ │ ├── 64.gif │ │ │ │ ├── 65.gif │ │ │ │ ├── 66.gif │ │ │ │ ├── 67.gif │ │ │ │ ├── 68.gif │ │ │ │ ├── 69.gif │ │ │ │ ├── 70.gif │ │ │ │ └── 71.gif │ │ ├── css │ │ │ └── modules │ │ │ │ ├── layer │ │ │ │ └── default │ │ │ │ │ ├── icon.png │ │ │ │ │ ├── icon-ext.png │ │ │ │ │ ├── loading-0.gif │ │ │ │ │ ├── loading-1.gif │ │ │ │ │ └── loading-2.gif │ │ │ │ └── code.css │ │ ├── lay │ │ │ └── modules │ │ │ │ ├── code.js │ │ │ │ ├── laytpl.js │ │ │ │ ├── flow.js │ │ │ │ ├── rate.js │ │ │ │ ├── util.js │ │ │ │ ├── carousel.js │ │ │ │ ├── laypage.js │ │ │ │ ├── transfer.js │ │ │ │ ├── slider.js │ │ │ │ ├── upload.js │ │ │ │ └── element.js │ │ └── layui.js │ └── font-awesome-4.7.0 │ │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ │ ├── less │ │ ├── screen-reader.less │ │ ├── fixed-width.less │ │ ├── larger.less │ │ ├── list.less │ │ ├── core.less │ │ ├── stacked.less │ │ ├── font-awesome.less │ │ ├── bordered-pulled.less │ │ ├── rotated-flipped.less │ │ ├── path.less │ │ ├── animated.less │ │ └── mixins.less │ │ ├── scss │ │ ├── _fixed-width.scss │ │ ├── _screen-reader.scss │ │ ├── _larger.scss │ │ ├── _list.scss │ │ ├── _core.scss │ │ ├── font-awesome.scss │ │ ├── _stacked.scss │ │ ├── _bordered-pulled.scss │ │ ├── _rotated-flipped.scss │ │ ├── _path.scss │ │ ├── _animated.scss │ │ └── _mixins.scss │ │ └── HELP-US-OUT.txt │ ├── js │ ├── lay-config.js │ └── lay-module │ │ └── layuimini │ │ └── miniTongji.js │ ├── css │ └── public.css │ └── config │ └── init.json ├── manage.py ├── templates ├── email.html ├── page │ ├── show_code.html │ ├── killer.html │ ├── show_file.html │ ├── email_report_setting.html │ └── timeline.html └── login.html ├── Dockerfile └── .gitignore /data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pekja/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layuimini/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /command/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /entities/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layuimini/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /command/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /parse/examples/sublist3r.txt: -------------------------------------------------------------------------------- 1 | blog.werner.wiki 2 | search.werner.wiki -------------------------------------------------------------------------------- /command/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'command.apps.CommandConfig' 2 | -------------------------------------------------------------------------------- /entities/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'entities.apps.EntitiesConfig' -------------------------------------------------------------------------------- /docker/env.example: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/docker/env.example -------------------------------------------------------------------------------- /docker/sources.list: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/docker/sources.list -------------------------------------------------------------------------------- /layuimini/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /layuimini/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /layuimini/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /screenshot/pekja-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/screenshot/pekja-web.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django==4.2.25 2 | django-import-export==2.0.2 3 | python-crontab==2.4.0 4 | psutil==5.7.0 -------------------------------------------------------------------------------- /screenshot/pekja-report.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/screenshot/pekja-report.jpg -------------------------------------------------------------------------------- /static/layuimini/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/images/bg.jpg -------------------------------------------------------------------------------- /static/layuimini/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/images/logo.png -------------------------------------------------------------------------------- /static/layuimini/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/images/favicon.ico -------------------------------------------------------------------------------- /layuimini/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LayuiminiConfig(AppConfig): 5 | name = 'layuimini' 6 | -------------------------------------------------------------------------------- /command/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CommandConfig(AppConfig): 5 | name = 'command' 6 | verbose_name = '命令' 7 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/font/iconfont.eot -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/font/iconfont.ttf -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/0.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/1.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/2.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/3.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/4.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/5.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/6.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/7.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/8.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/9.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/9.gif -------------------------------------------------------------------------------- /entities/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class EntitiesConfig(AppConfig): 5 | name = 'entities' 6 | verbose_name = 'Pekja' 7 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/font/iconfont.woff -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/font/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/font/iconfont.woff2 -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/10.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/11.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/12.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/13.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/13.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/14.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/14.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/15.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/15.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/16.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/17.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/17.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/18.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/18.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/19.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/19.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/20.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/20.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/21.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/21.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/22.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/22.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/23.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/23.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/24.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/24.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/25.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/25.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/26.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/26.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/27.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/27.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/28.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/28.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/29.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/29.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/30.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/30.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/31.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/31.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/32.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/32.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/33.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/33.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/34.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/34.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/35.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/35.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/36.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/36.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/37.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/37.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/38.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/38.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/39.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/39.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/40.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/40.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/41.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/41.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/42.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/42.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/43.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/43.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/44.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/44.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/45.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/45.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/46.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/46.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/47.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/47.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/48.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/48.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/49.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/49.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/50.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/50.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/51.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/51.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/52.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/52.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/53.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/53.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/54.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/54.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/55.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/55.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/56.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/56.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/57.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/57.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/58.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/58.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/59.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/59.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/60.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/60.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/61.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/61.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/62.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/62.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/63.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/63.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/64.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/64.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/65.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/65.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/66.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/66.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/67.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/67.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/68.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/68.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/69.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/69.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/70.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/70.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/images/face/71.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/images/face/71.gif -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/font-awesome-4.7.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /parse/examples/ctfr.txt: -------------------------------------------------------------------------------- 1 | bharal.werner.wiki 2 | blog.werner.wiki 3 | ms.werner.wiki 4 | poem.werner.wiki 5 | poetry.werner.wiki 6 | search.werner.wiki 7 | wiki.werner.wiki 8 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/font-awesome-4.7.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/icon.png -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/font-awesome-4.7.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/icon-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/icon-ext.png -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/loading-0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/loading-0.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/loading-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/loading-1.gif -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Werneror/pekja/HEAD/static/layuimini/lib/layui-v2.5.5/css/modules/layer/default/loading-2.gif -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /docker/update.sh: -------------------------------------------------------------------------------- 1 | git pull 2 | docker build -t pekja . 3 | docker stop pekja 4 | docker rm pekja 5 | docker run -d -p 8000:8000 --env-file docker/env -v /opt/pekja:/opt/pekja/data --restart=always --name pekja pekja:latest -------------------------------------------------------------------------------- /parse/examples/lijiejie_sub_domains_brute.txt: -------------------------------------------------------------------------------- 1 | mail.werner.wiki 123.58.177.51 2 | search.werner.wiki 119.28.89.38 3 | ycj.werner.wiki 119.28.89.38 4 | www.werner.wiki 119.28.89.38 -------------------------------------------------------------------------------- /parse/ctfr.py: -------------------------------------------------------------------------------- 1 | from .parser import Parser 2 | 3 | 4 | class CTFRParser(Parser): 5 | """python3 ctfr.py -d facebook.com -o /home/shei/subdomains_fb.txt 6 | for ctfr 86a804a""" 7 | 8 | def parse(self): 9 | with open(self.file_path) as f: 10 | for line in f: 11 | domain = line.strip() 12 | self.add_record(domain) 13 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/HELP-US-OUT.txt: -------------------------------------------------------------------------------- 1 | I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project, 2 | Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome, 3 | comprehensive icon sets or copy and paste your own. 4 | 5 | Please. Check it out. 6 | 7 | -Dave Gandy 8 | -------------------------------------------------------------------------------- /parse/examples/one_for_all.json: -------------------------------------------------------------------------------- 1 | www.github.com 2 | mail.github.com 3 | ftp.github.com 4 | smtp.github.com 5 | pop.github.com 6 | m.github.com 7 | webmail.github.com 8 | pop3.github.com 9 | imap.github.com 10 | localhost.github.com 11 | autodiscover.github.com 12 | admin.github.com 13 | bbs.github.com 14 | test.github.com 15 | mx.github.com 16 | en.github.com 17 | email.github.com 18 | wap.github.com -------------------------------------------------------------------------------- /command/management/commands/is_tool_empty.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from entities.models import Tool 4 | 5 | 6 | class Command(BaseCommand): 7 | help = 'Is the tool table empty' 8 | 9 | def handle(self, *args, **options): 10 | if Tool.objects.count() == 0: 11 | print('Yes') 12 | exit(0) 13 | else: 14 | print('No') 15 | exit(-1) 16 | -------------------------------------------------------------------------------- /pekja/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for pekja project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pekja.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /pekja/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for pekja project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pekja.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /parse/sublist3r_parser.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from .parser import Parser 3 | 4 | 5 | class Sublist3rParser(Parser): 6 | """/usr/local/bin/python /opt/Sublist3r/sublist3r.py -d {input} -o {output_file} 7 | for https://github.com/aboul3la/Sublist3r 61ebf36""" 8 | 9 | def parse(self): 10 | with open(self.file_path) as f: 11 | for line in f: 12 | domain = line.strip() 13 | if domain: 14 | self.add_record(domain) 15 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /command/management/commands/parse.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from command.cron_task import run_parse 4 | from pekja.utils import get_task_by_id 5 | 6 | 7 | class Command(BaseCommand): 8 | help = 'Parse the output of a task' 9 | 10 | def add_arguments(self, parser): 11 | parser.add_argument('task_id', type=int) 12 | 13 | def handle(self, *args, **options): 14 | task = get_task_by_id(options['task_id']) 15 | if task: 16 | run_parse(task) 17 | -------------------------------------------------------------------------------- /parse/nmap_dns_brute.py: -------------------------------------------------------------------------------- 1 | try: 2 | import xml.etree.cElementTree as ET 3 | except ImportError: 4 | import xml.etree.ElementTree as ET 5 | 6 | from .parser import Parser 7 | 8 | 9 | class NmapDnsBruteParser(Parser): 10 | """nmap -sn -Pn --script=dns-brute {input} -oX {output_file} 11 | for nmap 7.7.0""" 12 | 13 | def parse(self): 14 | tree = ET.parse(self.file_path) 15 | root = tree.getroot() 16 | for domain in root.findall(".//*[@key='hostname']"): 17 | self.add_record(domain.text) 18 | -------------------------------------------------------------------------------- /parse/one_for_all.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import json 4 | 5 | from .parser import Parser 6 | 7 | 8 | class OneForAllParser(Parser): 9 | """/usr/local/bin/python /opt/oneforall/oneforall/oneforall.py --target {input} --out {output_file} --format json run 10 | for OneForAll.py v0.3.0""" 11 | 12 | def parse(self): 13 | with open(self.file_path, encoding='utf-8') as f: 14 | for line in f: 15 | domain = line.strip() 16 | if domain: 17 | self.add_record(domain) 18 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /parse/lijiejie_subdomains_brute.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from .parser import Parser 3 | 4 | 5 | class LijiejieSubDomainsBrute(Parser): 6 | """/usr/local/bin/python2 /opt/subDomainsBrute/subDomainsBrute.py --full -o {output_file} {input} 7 | for https://github.com/lijiejie/subDomainsBrute bac5eb3""" 8 | 9 | def parse(self): 10 | with open(self.file_path) as f: 11 | for line in f: 12 | domain = line.split('\t')[0].strip() 13 | if domain: 14 | self.add_record(domain) 15 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /parse/censys_enumeration_email.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from .parser import Parser 4 | 5 | 6 | class CensysEnumerationEmail(Parser): 7 | """python censys_enumeration.py --outfile {output_file} {input} 8 | for censys_enumeration.py 10d42fa3""" 9 | 10 | def parse(self): 11 | with open(self.file_path) as f: 12 | output = json.loads(f.read()) 13 | for domain in output: 14 | sub_domains = output[domain].get('emails', list()) 15 | for sub_domain in sub_domains: 16 | self.add_record(sub_domain) 17 | -------------------------------------------------------------------------------- /parse/censys_enumeration_domain.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from .parser import Parser 4 | 5 | 6 | class CensysEnumerationDomain(Parser): 7 | """python censys_enumeration.py --outfile {output_file} {input} 8 | for censys_enumeration.py 10d42fa3""" 9 | 10 | def parse(self): 11 | with open(self.file_path) as f: 12 | output = json.loads(f.read()) 13 | for domain in output: 14 | sub_domains = output[domain].get('subdomains', list()) 15 | for sub_domain in sub_domains: 16 | self.add_record(sub_domain) 17 | -------------------------------------------------------------------------------- /command/management/commands/cron_all_task.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from entities.models import Task 4 | from entities.models import BatchTask 5 | from command.cron_task import set_cron_task 6 | from command.cron_task import set_cron_batch_task 7 | 8 | 9 | class Command(BaseCommand): 10 | help = 'Add all tasks to crontab' 11 | 12 | def handle(self, *args, **options): 13 | for task in Task.objects.all(): 14 | set_cron_task(task) 15 | for batch_task in BatchTask.objects.all(): 16 | set_cron_batch_task(batch_task) 17 | -------------------------------------------------------------------------------- /entities/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from parse import get_parse_class_list 3 | 4 | 5 | class ToolForm(forms.ModelForm): 6 | 7 | parse_class_name = forms.ChoiceField(choices=(), label='输出解析类') 8 | 9 | # 必须重写__init__方法,这样才能每次实例化表单时重新获取选项 10 | def __init__(self, *args, **kwargs): 11 | super(ToolForm, self).__init__(*args, **kwargs) 12 | self.fields['parse_class_name'].choices = ToolForm.get_parse_class_choices() 13 | 14 | @classmethod 15 | def get_parse_class_choices(cls): 16 | return tuple([(class_name, class_name) for class_name in get_parse_class_list()]) 17 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /entities/validators.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ValidationError 2 | from crontab import CronSlices 3 | 4 | 5 | def cron_validator(value): 6 | """ 7 | 校验是否是有效的Cron表达式 8 | :param value: 9 | :return: 10 | """ 11 | if not CronSlices.is_valid(value): 12 | raise ValidationError('不是有效的Crontab表达式') 13 | 14 | 15 | def command_validator(value): 16 | """ 17 | 校验命令中是否包含输入文件和输出文件占位符 18 | :param value: 19 | :return: 20 | """ 21 | if '{input}' not in value or '{output_file}' not in value: 22 | raise ValidationError('命令中必须包含输入占位符`{input}`和输出文件占位符`{output_file}`') 23 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | @import "screen-reader.less"; 19 | -------------------------------------------------------------------------------- /command/management/commands/update_input.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from command.cron_task import update_dynamic_input 4 | from pekja.utils import get_task_by_id 5 | 6 | 7 | class Command(BaseCommand): 8 | help = 'Update task input' 9 | 10 | def add_arguments(self, parser): 11 | parser.add_argument('task_id', type=int) 12 | 13 | def handle(self, *args, **options): 14 | task = get_task_by_id(options['task_id']) 15 | if task: 16 | update_dynamic_input(task) 17 | else: 18 | print('Task with ID {} does not exist'.format(options['task_id'])) 19 | -------------------------------------------------------------------------------- /parse/nmap_syn_scan.py: -------------------------------------------------------------------------------- 1 | try: 2 | import xml.etree.cElementTree as ET 3 | except ImportError: 4 | import xml.etree.ElementTree as ET 5 | 6 | from .parser import Parser 7 | 8 | 9 | class NmapSynScanParser(Parser): 10 | """nmap -sS -p- -iL {input} -oX {output_file} --open 11 | for nmap 7.7.0""" 12 | 13 | def parse(self): 14 | tree = ET.parse(self.file_path) 15 | root = tree.getroot() 16 | for host in root.findall('host'): 17 | ipv4 = host.findall("address[@addrtype='ipv4']")[0].get('addr') 18 | for port in host.findall(".//port"): 19 | record = '{}:{}'.format(ipv4, port.get('portid')) 20 | self.add_record(record) 21 | -------------------------------------------------------------------------------- /parse/nmap_udp_scan.py: -------------------------------------------------------------------------------- 1 | try: 2 | import xml.etree.cElementTree as ET 3 | except ImportError: 4 | import xml.etree.ElementTree as ET 5 | 6 | from .parser import Parser 7 | 8 | 9 | class NmapUdpScanParser(Parser): 10 | """nmap -sU -p- -iL {input} -oX {output_file} --open 11 | for nmap 7.7.0""" 12 | 13 | def parse(self): 14 | tree = ET.parse(self.file_path) 15 | root = tree.getroot() 16 | for host in root.findall('host'): 17 | ipv4 = host.findall("address[@addrtype='ipv4']")[0].get('addr') 18 | for port in host.findall(".//port"): 19 | record = '{}:{}'.format(ipv4, port.get('portid')) 20 | self.add_record(record) 21 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pekja.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { float: left; } 11 | .@{fa-css-prefix}-pull-right { float: right; } 12 | 13 | .@{fa-css-prefix} { 14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .@{fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /command/management/commands/record_report.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from django.core.management.base import BaseCommand 4 | 5 | from command.report import generate_new_record_report 6 | from command.report import send_report_by_mail 7 | 8 | 9 | class Command(BaseCommand): 10 | help = 'Generate record report and email' 11 | 12 | def add_arguments(self, parser): 13 | parser.add_argument('date', type=str, nargs='?') 14 | 15 | def handle(self, *args, **options): 16 | try: 17 | date = datetime.datetime.strptime('' if options.get('date') is None else options.get('date'), '%Y-%m-%d') 18 | except ValueError: 19 | date = datetime.datetime.now() 20 | report = generate_new_record_report(date) 21 | send_report_by_mail(date, report) 22 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /templates/email.html: -------------------------------------------------------------------------------- 1 | Pekja
{{title|safe}}

{{content|safe}}

祝:工作顺利,生活愉快!
pekja团队敬奉
https://github.com/werneror/pekja
-------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /parse/nmap_http_scan.py: -------------------------------------------------------------------------------- 1 | try: 2 | import xml.etree.cElementTree as ET 3 | except ImportError: 4 | import xml.etree.ElementTree as ET 5 | 6 | from .parser import Parser 7 | 8 | 9 | class NmapHTTPScanParser(Parser): 10 | """nmap -sV -sC -p 80,443,7000,8080 -iL {input} --open -oX {output_file} 11 | for nmap 7.7.0""" 12 | 13 | def parse(self): 14 | tree = ET.parse(self.file_path) 15 | root = tree.getroot() 16 | for host in root.findall('host'): 17 | hostname = host.find('hostnames').find('hostname[@type="user"]').get('name') 18 | for port in host.find('ports').findall('port'): 19 | port_id = port.get('portid') 20 | server = port.find('service').get('product') 21 | title = port.find('script[@id="http-title"]').get('output') 22 | self.add_record('{}:{}:{}:{}'.format(hostname, port_id, server, title)) 23 | -------------------------------------------------------------------------------- /pekja/urls.py: -------------------------------------------------------------------------------- 1 | """pekja URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | from django.urls import include 19 | 20 | import layuimini.urls 21 | 22 | 23 | urlpatterns = [ 24 | path('', include(layuimini.urls)), 25 | path('admin/', admin.site.urls), 26 | ] 27 | -------------------------------------------------------------------------------- /parse/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import inspect 3 | import pkgutil 4 | import importlib 5 | 6 | from .parser import Parser 7 | 8 | 9 | def get_parse_class_list(): 10 | pkg_path = os.path.dirname(__file__) 11 | parse_class_list = list() 12 | for _, file, _ in pkgutil.iter_modules([pkg_path]): 13 | module = importlib.import_module('parse.' + file) 14 | parse_class_list += [m[0] for m in inspect.getmembers(module, inspect.isclass) 15 | if issubclass(m[1], Parser) and m[0] != 'Parser'] 16 | return list(set(parse_class_list)) 17 | 18 | 19 | def get_parse_class(class_name): 20 | pkg_path = os.path.dirname(__file__) 21 | for _, file, _ in pkgutil.iter_modules([pkg_path]): 22 | module = importlib.import_module('parse.' + file) 23 | for m in inspect.getmembers(module, inspect.isclass): 24 | if issubclass(m[1], Parser) and m[0] == class_name: 25 | return m[1] 26 | -------------------------------------------------------------------------------- /command/management/commands/set_record_report_cron.py: -------------------------------------------------------------------------------- 1 | import os 2 | from crontab import CronSlices 3 | from django.core.management.base import BaseCommand 4 | from command.cron_task import set_cron_mail_report 5 | 6 | 7 | class Command(BaseCommand): 8 | help = 'Set when to send mail reports' 9 | 10 | def add_arguments(self, parser): 11 | parser.add_argument('dispatch_file_path', type=str) 12 | 13 | def handle(self, *args, **options): 14 | file_path = options.get('dispatch_file_path') 15 | if os.path.exists(file_path): 16 | with open(file_path) as f: 17 | dispatch = f.read() 18 | if CronSlices.is_valid(dispatch): 19 | set_cron_mail_report(dispatch) 20 | print('Successfully set the scheduled sending of mail Report.') 21 | else: 22 | print('Not a valid cron expression.') 23 | else: 24 | print('The file does not exist.') 25 | -------------------------------------------------------------------------------- /command/management/commands/init_admin.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | from django.contrib.auth import get_user_model 3 | 4 | 5 | class Command(BaseCommand): 6 | help = 'Create init admin user' 7 | 8 | def add_arguments(self, parser): 9 | parser.add_argument('username', type=str) 10 | parser.add_argument('email', type=str) 11 | parser.add_argument('password', type=str) 12 | 13 | def handle(self, *args, **options): 14 | user_model = get_user_model() 15 | if user_model.objects.count() == 0: 16 | admin = user_model.objects.create_superuser(username=options.get('username'), email=options.get('email'), 17 | password=options.get('password')) 18 | admin.is_active = True 19 | admin.is_admin = True 20 | admin.save() 21 | else: 22 | print('Admin accounts can only be initialized if no users exist') 23 | -------------------------------------------------------------------------------- /entities/widgets.py: -------------------------------------------------------------------------------- 1 | from import_export.widgets import Widget 2 | 3 | 4 | # Taken from https://github.com/django-import-export/django-import-export/issues/525#issuecomment-303046691 5 | class ChoicesWidget(Widget): 6 | """ 7 | Widget that uses choice display values in place of database values 8 | """ 9 | 10 | # pylint:disable=unused-argument 11 | def __init__(self, choices, *args, **kwargs): 12 | """ 13 | Creates a self.choices dict with a key, display value, and value, 14 | db value, e.g. {'Chocolate': 'CHOC'} 15 | """ 16 | self.choices = dict(choices) 17 | self.revert_choices = dict((v, k) for k, v in self.choices.items()) 18 | 19 | def clean(self, value, row=None, *args, **kwargs): 20 | """Returns the db value given the display value""" 21 | return self.revert_choices.get(value, value) if value else None 22 | 23 | def render(self, value, obj=None): 24 | """Returns the display value given the db value""" 25 | return self.choices.get(value, '') 26 | 27 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/css/modules/code.css: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none} -------------------------------------------------------------------------------- /static/layuimini/js/lay-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * date:2019/08/16 3 | * author:Mr.Chung 4 | * description:此处放layui自定义扩展 5 | */ 6 | 7 | window.rootPath = (function (src) { 8 | src = document.scripts[document.scripts.length - 1].src; 9 | return src.substring(0, src.lastIndexOf("/") + 1); 10 | })(); 11 | 12 | layui.config({ 13 | base: rootPath + "lay-module/", 14 | version: true 15 | }).extend({ 16 | miniAdmin: "layuimini/miniAdmin", // layuimini后台扩展 17 | miniMenu: "layuimini/miniMenu", // layuimini菜单扩展 18 | miniTab: "layuimini/miniTab", // layuimini tab扩展 19 | miniTheme: "layuimini/miniTheme", // layuimini 主题扩展 20 | miniTongji: "layuimini/miniTongji", // layuimini 统计扩展 21 | step: 'step-lay/step', // 分步表单扩展 22 | treetable: 'treetable-lay/treetable', //table树形扩展 23 | tableSelect: 'tableSelect/tableSelect', // table选择扩展 24 | iconPickerFa: 'iconPicker/iconPickerFa', // fa图标选择扩展 25 | echarts: 'echarts/echarts', // echarts图表扩展 26 | echartsTheme: 'echarts/echartsTheme', // echarts图表主题扩展 27 | wangEditor: 'wangEditor/wangEditor', // wangEditor富文本扩展 28 | layarea: 'layarea/layarea', // 省市县区三级联动下拉选择器 29 | }); -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/code.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
  1. '+o.replace(/[\r\t\n]+/g,"
  2. ")+"
"),c.find(">.layui-code-h3")[0]||c.prepend('

'+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); -------------------------------------------------------------------------------- /layuimini/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.index, name='index'), 7 | path('login/', views.user_login, name='user_login'), 8 | path('logout/', views.user_logout, name='user_logout'), 9 | path('api/clear/', views.api_clear, name='api_clear'), 10 | path('api/graph/', views.api_graph, name='api_graph'), 11 | path('api/input_file/', views.api_input_file, name='api_input_file'), 12 | path('api/output_file/', views.api_output_file, name='api_output_file'), 13 | path('download/', views.download, name='daownload'), 14 | path('page/dashboard/', views.dashboard, name='dashboard'), 15 | path('page/timeline/', views.timeline, name='timeline'), 16 | path('page/crontab/', views.crontab, name='crontab'), 17 | path('page/email_report/', views.email_report, name='email_report'), 18 | path('page/input/', views.input_file, name='input_file'), 19 | path('page/output/', views.output_file, name='output_file'), 20 | path('page/mail/', views.var_mail, name='var_mail'), 21 | path('page/process/', views.process, name='process'), 22 | path('page/killer/', views.killer, name='killer'), 23 | ] 24 | -------------------------------------------------------------------------------- /static/layuimini/js/lay-module/layuimini/miniTongji.js: -------------------------------------------------------------------------------- 1 | /** 2 | * date:2020/03/01 3 | * author:Mr.Chung 4 | * version:2.0 5 | * description:layuimini 统计框架扩展 6 | */ 7 | layui.define(["jquery"], function (exports) { 8 | var $ = layui.$; 9 | 10 | var miniTongji = { 11 | 12 | /** 13 | * 初始化 14 | * @param options 15 | */ 16 | render: function (options) { 17 | options.specific = options.specific || false; 18 | options.domains = options.domains || []; 19 | var domain = window.location.hostname; 20 | if (options.specific === false || (options.specific === true && options.domains.indexOf(domain) >=0)) { 21 | miniTongji.listen(); 22 | } 23 | }, 24 | 25 | /** 26 | * 监听统计代码 27 | */ 28 | listen: function () { 29 | var _hmt = _hmt || []; 30 | (function () { 31 | var hm = document.createElement("script"); 32 | hm.src = "https://hm.baidu.com/hm.js?d97abf6d61c21d773f97835defbdef4e"; 33 | var s = document.getElementsByTagName("script")[0]; 34 | s.parentNode.insertBefore(hm, s); 35 | })(); 36 | } 37 | }; 38 | 39 | exports("miniTongji", miniTongji); 40 | }); -------------------------------------------------------------------------------- /command/report.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | from entities.models import Record 3 | from pekja.utils import send_mail_to_users 4 | from pekja.utils import rowspan_html_table 5 | 6 | 7 | def generate_new_record_report(date): 8 | """ 9 | 生成某天的新增记录报告 10 | :param date: 11 | :return: 12 | """ 13 | records = Record.objects.filter(add_time__year=date.year, add_time__month=date.month, add_time__day=date.day) 14 | 15 | data = dict() 16 | for record in records: 17 | if record.project.name not in data: 18 | data[record.project.name] = dict() 19 | if record.type not in data[record.project.name]: 20 | data[record.project.name][record.type] = 0 21 | data[record.project.name][record.type] += 1 22 | 23 | table_data = list() 24 | for project in data: 25 | for record_type in data[project]: 26 | table_data.append([project, record_type, data[project][record_type]]) 27 | 28 | return rowspan_html_table(['项目', '类型', '数量'], data) 29 | 30 | 31 | def send_report_by_mail(date, report): 32 | """ 33 | 通过邮件发送报告 34 | :param date: 35 | :param report: HTML 36 | :return: 37 | """ 38 | send_mail_to_users('【pekja】{}新增记录报告'.format(date.strftime('%Y-%m-%d')), 39 | '{}新增记录见下表。'.format(date.strftime('%Y-%m-%d')), report) 40 | -------------------------------------------------------------------------------- /parse/parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | from pekja.utils import get_output_file_path 4 | from entities.models import Record 5 | 6 | 7 | class Parser: 8 | 9 | def __init__(self, task, file_path=None): 10 | self.task = task 11 | if file_path is not None: 12 | self.file_path = file_path 13 | else: 14 | self.file_path = get_output_file_path(task) 15 | 16 | def parse(self): 17 | pass 18 | 19 | def rename_file(self): 20 | now = time.strftime('%Y-%m-%d %H-%M-%S', time.localtime(time.time())) 21 | os.rename(self.file_path, 22 | self.file_path.replace('-{}.txt'.format(self.task.id), '-{}-{}.txt'.format(self.task.id, now))) 23 | 24 | def add_record(self, record, record_type=None): 25 | """ 26 | 添加一条记录 27 | :param record: 记录内容 28 | :param record_type: 记录类型 29 | :return: 是否成功,错误消息 30 | """ 31 | if record_type is None: 32 | record_type = self.task.tool.type 33 | try: 34 | record = Record.objects.get(record=record, project=self.task.project, type=record_type) 35 | except Record.DoesNotExist: 36 | Record(record=record, project=self.task.project, type=record_type, source=self.task.tool.name).save() 37 | return True 38 | except Record.MultipleObjectsReturned: 39 | return False 40 | else: 41 | if self.task.tool.name not in record.source: 42 | record.source += ',' + self.task.tool.name 43 | record.save() # 保存一次是为了更新最后修改时间 44 | return True 45 | -------------------------------------------------------------------------------- /static/layuimini/css/public.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 15px 15px 15px 15px; 3 | background: #f2f2f2; 4 | } 5 | 6 | .layuimini-container { 7 | border: 1px solid #f2f2f2; 8 | border-radius: 5px; 9 | background-color: #ffffff 10 | } 11 | 12 | .layuimini-main { 13 | margin: 10px 10px 10px 10px; 14 | } 15 | 16 | /**必填红点 */ 17 | .layuimini-form > .layui-form-item > .required:after { 18 | content: '*'; 19 | color: red; 20 | position: absolute; 21 | margin-left: 4px; 22 | font-weight: bold; 23 | line-height: 1.8em; 24 | top: 6px; 25 | right: 5px; 26 | } 27 | 28 | .layuimini-form > .layui-form-item > .layui-form-label { 29 | width: 120px !important; 30 | } 31 | 32 | .layuimini-form > .layui-form-item > .layui-input-block { 33 | margin-left: 150px !important; 34 | } 35 | 36 | .layuimini-form > .layui-form-item > .layui-input-block > tip { 37 | display: inline-block; 38 | margin-top: 10px; 39 | line-height: 10px; 40 | font-size: 10px; 41 | color: #a29c9c; 42 | } 43 | 44 | /**搜索框*/ 45 | .layuimini-container .table-search-fieldset { 46 | margin: 0; 47 | border: 1px solid #e6e6e6; 48 | padding: 10px 20px 5px 20px; 49 | color: #6b6b6b; 50 | } 51 | 52 | /**自定义滚动条样式 */ 53 | ::-webkit-scrollbar { 54 | width: 6px; 55 | height: 6px 56 | } 57 | 58 | ::-webkit-scrollbar-track { 59 | background-color: transparent; 60 | -webkit-border-radius: 2em; 61 | -moz-border-radius: 2em; 62 | border-radius: 2em; 63 | } 64 | 65 | ::-webkit-scrollbar-thumb { 66 | background-color: #9c9da0; 67 | -webkit-border-radius: 2em; 68 | -moz-border-radius: 2em; 69 | border-radius: 2em 70 | } 71 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/laytpl.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)}); -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | .fa-icon-rotate(@degrees, @rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 16 | -webkit-transform: rotate(@degrees); 17 | -ms-transform: rotate(@degrees); 18 | transform: rotate(@degrees); 19 | } 20 | 21 | .fa-icon-flip(@horiz, @vert, @rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 23 | -webkit-transform: scale(@horiz, @vert); 24 | -ms-transform: scale(@horiz, @vert); 25 | transform: scale(@horiz, @vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | .sr-only() { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | .sr-only-focusable() { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /static/layuimini/lib/font-awesome-4.7.0/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/flow.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)}); -------------------------------------------------------------------------------- /parse/examples/nmap_sub_domain_brute.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /static/layuimini/config/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "homeInfo": { 3 | "title": "首页", 4 | "href": "page/dashboard/" 5 | }, 6 | "logoInfo": { 7 | "title": "pekja", 8 | "image": "/static/layuimini/images/logo.png", 9 | "href": "" 10 | }, 11 | "menuInfo": [ 12 | { 13 | "title": "数据展示", 14 | "icon": "fa fa-home", 15 | "href": "", 16 | "target": "_self", 17 | "child": [ 18 | { 19 | "title": "数据面板", 20 | "icon": "fa fa-tachometer", 21 | "href": "page/dashboard/", 22 | "target": "_self" 23 | }, 24 | { 25 | "title": "时间线", 26 | "icon": "fa fa-heartbeat", 27 | "href": "page/timeline/", 28 | "target": "_self" 29 | } 30 | ] 31 | }, 32 | { 33 | "title": "系统设置", 34 | "icon": "fa fa-gears", 35 | "href": "", 36 | "target": "_self", 37 | "child": [ 38 | { 39 | "title": "邮件报告", 40 | "icon": "fa fa-envelope", 41 | "href": "page/email_report/", 42 | "target": "_self" 43 | } 44 | ] 45 | }, 46 | { 47 | "title": "高级调试", 48 | "icon": "fa fa-bug", 49 | "href": "", 50 | "target": "_self", 51 | "child": [ 52 | { 53 | "title": "Crontab", 54 | "icon": "fa fa-calendar", 55 | "href": "page/crontab/", 56 | "target": "_self" 57 | }, 58 | { 59 | "title": "Input", 60 | "icon": "fa fa-hourglass-start", 61 | "href": "page/input/", 62 | "target": "_self" 63 | }, 64 | { 65 | "title": "Output", 66 | "icon": "fa fa-hourglass-end", 67 | "href": "page/output/", 68 | "target": "_self" 69 | }, 70 | { 71 | "title": "Mail", 72 | "icon": "fa fa-envelope-o", 73 | "href": "page/mail/", 74 | "target": "_self" 75 | }, 76 | { 77 | "title": "Process", 78 | "icon": "fa fa-list", 79 | "href": "page/process/", 80 | "target": "_self" 81 | }, 82 | { 83 | "title": "Killer", 84 | "icon": "fa fa-cutlery", 85 | "href": "page/killer/", 86 | "target": "_self" 87 | } 88 | ] 89 | } 90 | ] 91 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | # Set up for localization in China. If you are not in China, please delete this part 4 | COPY docker/sources.list /etc/apt/sources.list 5 | RUN pip install -U pip setuptools -i https://pypi.tuna.tsinghua.edu.cn/simple 6 | RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 7 | RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 8 | RUN dpkg-reconfigure -f noninteractive tzdata 9 | 10 | # Update 11 | RUN apt-get update && apt-get upgrade -y 12 | 13 | # Install crontab 14 | RUN apt-get install -y cron 15 | RUN service cron start 16 | RUN update-rc.d cron defaults 17 | 18 | # Clear /opt 19 | RUN rm -rf /opt 20 | 21 | # Install tool: nmap 22 | RUN apt-get install -y nmap 23 | 24 | # Install censys-enumeration 25 | RUN mkdir -p /opt/censys_enumeration 26 | RUN git clone https://github.com/0xbharath/censys-enumeration.git /opt/censys_enumeration 27 | RUN pip install -r /opt/censys_enumeration/requirements.txt 28 | 29 | # Install CTFR 30 | RUN mkdir -p /opt/ctfr 31 | RUN git clone https://github.com/UnaPibaGeek/ctfr.git /opt/ctfr 32 | RUN pip install -r /opt/ctfr/requirements.txt 33 | 34 | # Install OneForAll 35 | RUN mkdir -p /opt/oneforall 36 | RUN wget https://github.com/shmilylty/OneForAll/archive/v0.3.0.tar.gz -O /opt/oneforall/v0.3.0.tar.gz 37 | RUN tar xzf /opt/oneforall/v0.3.0.tar.gz -C /opt/oneforall/ 38 | RUN mv /opt/oneforall/OneForAll-0.3.0/* /opt/oneforall 39 | RUN rm -rf /opt/oneforall/OneForAll-0.3.0 /opt/oneforall/v0.3.0.tar.gz 40 | RUN apt install -y python3-testresources 41 | RUN pip install -r /opt/oneforall/requirements.txt 42 | 43 | # Install lijiejie/subDomainsBrute 44 | RUN mkdir -p /opt/subDomainsBrute 45 | RUN git clone https://github.com/lijiejie/subDomainsBrute.git /opt/subDomainsBrute 46 | RUN apt install -y python-pip 47 | RUN python2 -m pip install dnspython gevent 48 | 49 | # Install Sublist3r 50 | RUN mkdir -p /opt/Sublist3r 51 | RUN git clone https://github.com/aboul3la/Sublist3r.git /opt/Sublist3r 52 | RUN pip install -r /opt/Sublist3r/requirements.txt 53 | 54 | # Install pekja 55 | RUN mkdir -p /opt/pekja 56 | COPY requirements.txt /opt/pekja/requirements.txt 57 | RUN pip install -r /opt/pekja/requirements.txt 58 | COPY . /opt/pekja 59 | RUN chmod +x /opt/pekja/docker/start.sh 60 | 61 | # Run pekja 62 | WORKDIR /opt/pekja 63 | EXPOSE 8000 64 | ENTRYPOINT ["/opt/pekja/docker/start.sh"] 65 | -------------------------------------------------------------------------------- /templates/page/show_code.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Show Code 6 | 7 | 8 | {% load static %} 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
{{ code }}
32 |
33 |
34 |
35 |
36 |
37 |
38 | 39 | 40 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /.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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | pip-wheel-metadata/ 23 | share/python-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 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | data/db.sqlite3 61 | db.sqlite3-journal 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # IPython 80 | profile_default/ 81 | ipython_config.py 82 | 83 | # pyenv 84 | .python-version 85 | 86 | # pipenv 87 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 88 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 89 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 90 | # install all needed dependencies. 91 | #Pipfile.lock 92 | 93 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 94 | __pypackages__/ 95 | 96 | # Celery stuff 97 | celerybeat-schedule 98 | celerybeat.pid 99 | 100 | # SageMath parsed files 101 | *.sage.py 102 | 103 | # Environments 104 | .env 105 | .venv 106 | docker/env.example/ 107 | venv/ 108 | docker/env.example/ 109 | env.bak/ 110 | venv.bak/ 111 | 112 | # Spyder project settings 113 | .spyderproject 114 | .spyproject 115 | 116 | # Rope project settings 117 | .ropeproject 118 | 119 | # mkdocs documentation 120 | /site 121 | 122 | # mypy 123 | .mypy_cache/ 124 | .dmypy.json 125 | dmypy.json 126 | 127 | # Pyre type checker 128 | .pyre/ 129 | 130 | # Custom 131 | venv/ 132 | .idea/ 133 | *.log 134 | data/*.txt 135 | -------------------------------------------------------------------------------- /docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set timezone and restart crontab 4 | echo TZ=$(cat /etc/timezone) >> /etc/crontab 5 | echo CRON_TZ=$(cat /etc/timezone) >> /etc/crontab 6 | service cron restart 7 | 8 | # Set OneForAll's API 9 | echo "censys_api_id = '$CENSYS_API_ID'" >> /opt/oneforall/api.py 10 | echo "censys_api_secret = '$CENSYS_API_SECRET'" >> /opt/oneforall/api.py 11 | echo "binaryedge_api = '$BINARYEDGE_API'" >> /opt/oneforall/api.py 12 | echo "chinaz_api = '$CHINAZ_API'" >> /opt/oneforall/api.py 13 | echo "bing_api_id = '$BING_API_ID'" >> /opt/oneforall/api.py 14 | echo "bing_api_key = '$BING_API_KEY'" >> /opt/oneforall/api.py 15 | echo "securitytrails_api = '$SECURITYTRAILS_API'" >> /opt/oneforall/api.py 16 | echo "fofa_api_email = '$FOFA_API_EMAIL'" >> /opt/oneforall/api.py 17 | echo "fofa_api_key = '$FOFA_API_KEY'" >> /opt/oneforall/api.py 18 | echo "google_api_key = '$GOOGLE_API_KEY'" >> /opt/oneforall/api.py 19 | echo "google_api_cx = '$GOOGLE_API_CX'" >> /opt/oneforall/api.py 20 | echo "riskiq_api_username = '$RISKIQ_API_USERNAME'" >> /opt/oneforall/api.py 21 | echo "riskiq_api_key = '$RISKIQ_API_KEY'" >> /opt/oneforall/api.py 22 | echo "shodan_api_key = '$SHODAN_API_KEY'" >> /opt/oneforall/api.py 23 | echo "threatbook_api_key = '$THREATBOOK_API_KEY'" >> /opt/oneforall/api.py 24 | echo "virustotal_api_key = '$VIRUSTOTAL_API_KEY'" >> /opt/oneforall/api.py 25 | echo "zoomeye_api_usermail = '$ZOOMEYE_API_USERMAIL'" >> /opt/oneforall/api.py 26 | echo "zoomeye_api_password = '$ZOOMEYE_API_PASSWORD'" >> /opt/oneforall/api.py 27 | echo "spyse_api_token = '$SPYSE_API_TOKEN'" >> /opt/oneforall/api.py 28 | echo "circl_api_username = '$CIRCL_API_USERNAME'" >> /opt/oneforall/api.py 29 | echo "circl_api_password = '$CIRCL_API_PASSWORD'" >> /opt/oneforall/api.py 30 | echo "dnsdb_api_key = '$DNSDB_API_KEY'" >> /opt/oneforall/api.py 31 | echo "ipv4info_api_key = '$IPV4INFO_API_KEY'" >> /opt/oneforall/api.py 32 | echo "passivedns_api_addr = '$PASSIVEDNS_API_ADDR'" >> /opt/oneforall/api.py 33 | echo "passivedns_api_token = '$PASSIVEDNS_API_TOKEN'" >> /opt/oneforall/api.py 34 | echo "github_api_user = '$GITHUB_API_USER'" >> /opt/oneforall/api.py 35 | echo "github_api_token = '$GITHUB_API_TOKEN'" >> /opt/oneforall/api.py 36 | echo "github_email = '$GITHUB_EMAIL'" >> /opt/oneforall/api.py 37 | echo "github_password = '$GITHUB_PASSWORD'" >> /opt/oneforall/api.py 38 | 39 | # Start pekja 40 | python manage.py migrate --noinput 41 | python manage.py init_admin $INIT_ADMIN_USER $INIT_ADMIN_EMAIL $INIT_ADMIN_PASSWORD 42 | python manage.py loaddata docker/tool.json 43 | python manage.py cron_all_task 44 | python manage.py set_record_report_cron data/mail_report_dispatch.txt 45 | python manage.py runserver 0.0.0.0:8000 46 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/rate.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define("jquery",function(e){"use strict";var a=layui.jquery,i={config:{},index:layui.rate?layui.rate.index+1e4:0,set:function(e){var i=this;return i.config=a.extend({},i.config,e),i},on:function(e,a){return layui.onevent.call(this,n,e,a)}},l=function(){var e=this,a=e.config;return{setvalue:function(a){e.setvalue.call(e,a)},config:a}},n="rate",t="layui-rate",o="layui-icon-rate",s="layui-icon-rate-solid",u="layui-icon-rate-half",r="layui-icon-rate-solid layui-icon-rate-half",c="layui-icon-rate-solid layui-icon-rate",f="layui-icon-rate layui-icon-rate-half",v=function(e){var l=this;l.index=++i.index,l.config=a.extend({},l.config,i.config,e),l.render()};v.prototype.config={length:5,text:!1,readonly:!1,half:!1,value:0,theme:""},v.prototype.render=function(){var e=this,i=e.config,l=i.theme?'style="color: '+i.theme+';"':"";i.elem=a(i.elem),parseInt(i.value)!==i.value&&(i.half||(i.value=Math.ceil(i.value)-i.value<.5?Math.ceil(i.value):Math.floor(i.value)));for(var n='
    ",u=1;u<=i.length;u++){var r='
  • ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+'
  • ":n+=r}n+="
"+(i.text?''+i.value+"星":"")+"";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)}); -------------------------------------------------------------------------------- /templates/page/killer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 结束进程 6 | 7 | 8 | {% load static %} 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 | {% csrf_token %} 34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | {% if error_msg %} 53 | 54 | 55 | 61 | {% endif %} 62 | 63 | 64 | -------------------------------------------------------------------------------- /docker/tool.json: -------------------------------------------------------------------------------- 1 | [{"model": "entities.tool", "pk": 1, "fields": {"name": "Nmap\u5b50\u57df\u540d\u7206\u7834", "link": "https://nmap.org/", "type": "\u57df\u540d", "parse_class_name": "NmapDnsBruteParser", "command": "nmap -sn -Pn --script=dns-brute {input} -oX {output_file}", "input_type": "parameter", "version": "7.7.0", "comment": ""}}, {"model": "entities.tool", "pk": 2, "fields": {"name": "Nmap SYN\u626b\u63cf", "link": "https://nmap.org/", "type": "TCP\u7aef\u53e3", "parse_class_name": "NmapSynScanParser", "command": "nmap -sS -p- -iL {input} -oX {output_file} --open", "input_type": "file", "version": "7.7.0", "comment": ""}}, {"model": "entities.tool", "pk": 3, "fields": {"name": "Nmap UDP\u7aef\u53e3\u626b\u63cf", "link": "https://nmap.org/", "type": "UDP\u7aef\u53e3", "parse_class_name": "NmapUdpScanParser", "command": "nmap -sU -p- -iL {input} -oX {output_file} --open", "input_type": "file", "version": "7.7.0", "comment": ""}}, {"model": "entities.tool", "pk": 4, "fields": {"name": "Censys\u5b50\u57df\u540d\u91c7\u96c6", "link": "https://github.com/0xbharath/censys-enumeration/", "type": "\u57df\u540d", "parse_class_name": "CensysEnumerationDomain", "command": "/usr/local/bin/python /opt/censys_enumeration/censys_enumeration.py --outfile {output_file} {input}", "input_type": "file", "version": "10d42fa3", "comment": ""}}, {"model": "entities.tool", "pk": 5, "fields": {"name": "Censys\u90ae\u7bb1\u91c7\u96c6", "link": "https://github.com/0xbharath/censys-enumeration/", "type": "\u90ae\u7bb1", "parse_class_name": "CensysEnumerationEmail", "command": "/usr/local/bin/python /opt/censys_enumeration/censys_enumeration.py --outfile {output_file} {input}", "input_type": "file", "version": "10d42fa3", "comment": ""}}, {"model": "entities.tool", "pk": 6, "fields": {"name": "CTFR", "link": "https://github.com/UnaPibaGeek/ctfr", "type": "\u57df\u540d", "parse_class_name": "CTFRParser", "command": "/usr/local/bin/python /opt/ctfr/ctfr.py -d {input} -o {output_file}", "input_type": "parameter", "version": "86a804a", "comment": ""}}, {"model": "entities.tool", "pk": 7, "fields": {"name": "OneForAll\u5b50\u57df\u540d\u6536\u96c6", "link": "https://github.com/shmilylty/OneForAll", "type": "\u57df\u540d", "parse_class_name": "OneForAllParser", "command": "/usr/local/bin/python /opt/oneforall/oneforall.py --target {input} --path {output_file} --format json run", "input_type": "file", "version": "0.3.0", "comment": "\u5b58\u5728\u8fd0\u884c\u5b8c\u540e\u65e0\u6cd5\u9000\u51fa\u7684BUG"}}, {"model": "entities.tool", "pk": 8, "fields": {"name": "lijiejie/subDomainsBrute", "link": "https://github.com/lijiejie/subDomainsBrute", "type": "\u57df\u540d", "parse_class_name": "LijiejieSubDomainsBrute", "command": "cd /opt/subDomainsBrute;/usr/bin/python2 subDomainsBrute.py --full -o {output_file} {input}", "input_type": "parameter", "version": "bac5eb3", "comment": ""}}, {"model": "entities.tool", "pk": 9, "fields": {"name": "Sublist3r", "link": "https://github.com/aboul3la/Sublist3r", "type": "\u57df\u540d", "parse_class_name": "Sublist3rParser", "command": "/usr/local/bin/python /opt/Sublist3r/sublist3r.py -d {input} -e Baidu,Yahoo,Bing,Ask,Netcraft,DNSdumpster,Virustotal,ThreatCrowd,SSL,PassiveDNS -b -o {output_file}", "input_type": "parameter", "version": "61ebf36", "comment": ""}}, {"model": "entities.tool", "pk": 11, "fields": {"name": "Nmap HTTP\u626b\u63cf", "link": "https://nmap.org/", "type": "\u7f51\u7ad9", "parse_class_name": "NmapHTTPScanParser", "command": "nmap -sV -sC -p 80,443,7000,8080 -iL {input} --open -oX {output_file}", "input_type": "file", "version": "7.7.0", "comment": ""}}] -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/util.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define("jquery",function(t){"use strict";var e=layui.$,i={fixbar:function(t){var i,n,a="layui-fixbar",o="layui-fixbar-top",r=e(document),l=e("body");t=e.extend({showHeight:200},t),t.bar1=t.bar1===!0?"":t.bar1,t.bar2=t.bar2===!0?"":t.bar2,t.bgcolor=t.bgcolor?"background-color:"+t.bgcolor:"";var c=[t.bar1,t.bar2,""],g=e(['
    ',t.bar1?'
  • '+c[0]+"
  • ":"",t.bar2?'
  • '+c[1]+"
  • ":"",'
  • '+c[2]+"
  • ","
"].join("")),s=g.find("."+o),u=function(){var e=r.scrollTop();e>=t.showHeight?i||(s.show(),i=1):i&&(s.hide(),i=0)};e("."+a)[0]||("object"==typeof t.css&&g.css(t.css),l.append(g),u(),g.find("li").on("click",function(){var i=e(this),n=i.attr("lay-type");"top"===n&&e("html,body").animate({scrollTop:0},200),t.click&&t.click.call(this,n)}),r.on("scroll",function(){clearTimeout(n),n=setTimeout(function(){u()},100)}))},countdown:function(t,e,i){var n=this,a="function"==typeof e,o=new Date(t).getTime(),r=new Date(!e||a?(new Date).getTime():e).getTime(),l=o-r,c=[Math.floor(l/864e5),Math.floor(l/36e5)%24,Math.floor(l/6e4)%60,Math.floor(l/1e3)%60];a&&(i=e);var g=setTimeout(function(){n.countdown(t,r+1e3,i)},1e3);return i&&i(l>0?c:[0,0,0,0],e,g),l<=0&&clearTimeout(g),g},timeAgo:function(t,e){var i=this,n=[[],[]],a=(new Date).getTime()-new Date(t).getTime();return a>6912e5?(a=new Date(t),n[0][0]=i.digit(a.getFullYear(),4),n[0][1]=i.digit(a.getMonth()+1),n[0][2]=i.digit(a.getDate()),e||(n[1][0]=i.digit(a.getHours()),n[1][1]=i.digit(a.getMinutes()),n[1][2]=i.digit(a.getSeconds())),n[0].join("-")+" "+n[1].join(":")):a>=864e5?(a/1e3/60/60/24|0)+"天前":a>=36e5?(a/1e3/60/60|0)+"小时前":a>=12e4?(a/1e3/60|0)+"分钟前":a<0?"未来":"刚刚"},digit:function(t,e){var i="";t=String(t),e=e||2;for(var n=t.length;n/g,">").replace(/'/g,"'").replace(/"/g,""")},event:function(t,n,a){n=i.event[t]=e.extend(!0,i.event[t],n)||{},e("body").on(a||"click","*["+t+"]",function(){var i=e(this),a=i.attr(t);n[a]&&n[a].call(this,i)})}};!function(t,e,i){"$:nomunge";function n(){a=e[l](function(){o.each(function(){var e=t(this),i=e.width(),n=e.height(),a=t.data(this,g);(i!==a.w||n!==a.h)&&e.trigger(c,[a.w=i,a.h=n])}),n()},r[s])}var a,o=t([]),r=t.resize=t.extend(t.resize,{}),l="setTimeout",c="resize",g=c+"-special-event",s="delay",u="throttleWindow";r[s]=250,r[u]=!0,t.event.special[c]={setup:function(){if(!r[u]&&this[l])return!1;var e=t(this);o=o.add(e),t.data(this,g,{w:e.width(),h:e.height()}),1===o.length&&n()},teardown:function(){if(!r[u]&&this[l])return!1;var e=t(this);o=o.not(e),e.removeData(g),o.length||clearTimeout(a)},add:function(e){function n(e,n,o){var r=t(this),l=t.data(this,g)||{};l.w=n!==i?n:r.width(),l.h=o!==i?o:r.height(),a.apply(this,arguments)}if(!r[u]&&this[l])return!1;var a;return t.isFunction(e)?(a=e,n):(a=e.handler,void(e.handler=n))}}}(e,window),t("util",i)}); -------------------------------------------------------------------------------- /entities/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.utils.html import format_html 3 | from django.utils.html import escape 4 | 5 | from import_export.admin import ImportExportActionModelAdmin 6 | 7 | from command.cron_task import set_cron_task 8 | from command.cron_task import set_cron_batch_task 9 | from .forms import ToolForm 10 | from .resources import * 11 | 12 | 13 | @admin.register(Project) 14 | class ProjectAdmin(ImportExportActionModelAdmin): 15 | list_display = ['name', 'src_link_url'] 16 | search_fields = ['name', 'comment'] 17 | resource_class = ProjectResource 18 | 19 | def src_link_url(self, obj): 20 | if obj.src_link: 21 | url = obj.src_link.replace('"', r'%22') 22 | return format_html('{}'.format(url, escape(obj.src_link))) 23 | else: 24 | return obj.src_link 25 | 26 | src_link_url.short_description = 'SRC链接' 27 | 28 | 29 | @admin.register(Record) 30 | class RecordAdmin(ImportExportActionModelAdmin): 31 | list_display = ['record', 'project', 'add_time', 'last_modify_time', 'type', 'source'] 32 | search_fields = ['record', 'source'] 33 | list_filter = ('project', 'type', 'source') 34 | resource_class = RecordResource 35 | 36 | 37 | @admin.register(Tool) 38 | class ToolAdmin(ImportExportActionModelAdmin): 39 | list_display = ['name', 'link_url', 'version', 'type', 'parse_class_name', 'command', 'input_type', 'comment'] 40 | search_fields = ['name', 'command', 'comment'] 41 | list_filter = ('type', 'input_type') 42 | resource_class = ToolResource 43 | form = ToolForm 44 | 45 | def save_model(self, request, obj, form, change): 46 | obj.save() 47 | for task in obj.task_set.all(): 48 | set_cron_task(task) 49 | for i in range(1, BatchTask.MAX_TASK_AMOUNT + 1): 50 | for batch_task in getattr(task, 'task{}'.format(i)).all(): 51 | set_cron_batch_task(batch_task) 52 | 53 | def link_url(self, obj): 54 | if obj.link: 55 | url = obj.link.replace('"', r'%22') 56 | return format_html('{}'.format(url, escape(obj.link))) 57 | else: 58 | return obj.link 59 | 60 | link_url.short_description = '项目链接' 61 | 62 | 63 | @admin.register(Task) 64 | class TaskAdmin(ImportExportActionModelAdmin): 65 | list_display = ['id', 'name', 'project', 'tool', 'input_overview', 'dispatch', 'active'] 66 | search_fields = ['name', 'input'] 67 | list_filter = ('project', 'tool', 'active') 68 | resource_class = TaskResource 69 | 70 | def save_model(self, request, obj, form, change): 71 | obj.save() 72 | set_cron_task(obj) 73 | for i in range(1, BatchTask.MAX_TASK_AMOUNT+1): 74 | for batch_task in getattr(obj, 'task{}'.format(i)).all(): 75 | set_cron_batch_task(batch_task) 76 | 77 | def input_overview(self, obj): 78 | if len(obj.input) > 20: 79 | return obj.input[:20] + '...' 80 | else: 81 | return obj.input 82 | input_overview.short_description = '输入' 83 | 84 | 85 | @admin.register(BatchTask) 86 | class BatchTaskAdmin(ImportExportActionModelAdmin): 87 | list_display = ['id', 'name', 'task1', 'task2', 'task3', 'task4', 'task5', 'task6', 'task7', 'task8', 'task9', 88 | 'task10', 'dispatch', 'active'] 89 | search_fields = ['name'] 90 | list_filter = ('active', ) 91 | resource_class = BatchTaskResource 92 | 93 | def save_model(self, request, obj, form, change): 94 | obj.save() 95 | set_cron_batch_task(obj) 96 | 97 | 98 | admin.site.site_header = 'Pekja' 99 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/carousel.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(clearInterval(e.timer),e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
    ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
"].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a 2 | 3 | 4 | 5 | 文件查看 6 | 7 | 8 | {% load static %} 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /pekja/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for pekja project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.0.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '!2v^7t9b+%1xqeecwni&b3&y=%kj+80#l1lyd1&f)d3-ukcv3&' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['*'] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'import_export', 41 | 'layuimini', 42 | 'entities', 43 | 'command', 44 | ] 45 | 46 | MIDDLEWARE = [ 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'pekja.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'pekja.wsgi.application' 75 | 76 | 77 | # Database 78 | # https://docs.djangoproject.com/en/3.0/ref/settings/#databases 79 | 80 | DATABASES = { 81 | 'default': { 82 | 'ENGINE': 'django.db.backends.sqlite3', 83 | 'NAME': os.path.join(BASE_DIR, 'data', 'db.sqlite3'), 84 | } 85 | } 86 | 87 | 88 | # Password validation 89 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators 90 | 91 | AUTH_PASSWORD_VALIDATORS = [ 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 103 | }, 104 | ] 105 | 106 | 107 | # Internationalization 108 | # https://docs.djangoproject.com/en/3.0/topics/i18n/ 109 | 110 | LANGUAGE_CODE = 'zh-Hans' 111 | 112 | TIME_ZONE = 'Asia/Shanghai' 113 | 114 | USE_I18N = True 115 | 116 | USE_L10N = True 117 | 118 | USE_TZ = True 119 | 120 | 121 | # Static files (CSS, JavaScript, Images) 122 | # https://docs.djangoproject.com/en/3.0/howto/static-files/ 123 | 124 | STATIC_URL = '/static/' 125 | STATICFILES_DIRS = [ 126 | os.path.join(BASE_DIR, "static"), 127 | ] 128 | 129 | DATA_DIRS = os.path.join(BASE_DIR, "data") 130 | CRON_USER = True # 为True表示当前用户,也可以写具体的用户名 131 | 132 | EMAIL_USE_SSL = True if os.environ.get('EMAIL_USE_SSL') else False 133 | EMAIL_HOST = os.environ.get('EMAIL_HOST') 134 | EMAIL_PORT = os.environ.get('EMAIL_PORT') 135 | EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER') 136 | EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD') 137 | DEFAULT_FROM_EMAIL = 'Pekja <{}>'.format(EMAIL_HOST_USER) 138 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/laypage.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),refresh:['','',""].join(""),skip:function(){return['到第','','页',""].join("")}()};return['
',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
"].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)}); -------------------------------------------------------------------------------- /templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | pekja管理-登录 6 | 7 | 8 | 9 | 10 | 11 | {% load static %} 12 | 13 | 17 | 36 | 37 | 38 |
39 |
40 | 57 |
58 | 59 | 86 | 87 | -------------------------------------------------------------------------------- /pekja/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from html import escape 3 | from platform import system 4 | 5 | from crontab import CronTab 6 | 7 | from django.core.mail import EmailMessage 8 | from django.template.loader import render_to_string 9 | from django.contrib.auth import get_user_model 10 | 11 | from pekja.settings import DEFAULT_FROM_EMAIL 12 | from pekja.settings import DATA_DIRS 13 | from pekja.settings import CRON_USER 14 | from entities.models import Task 15 | 16 | 17 | def get_input_file_path(obj): 18 | return os.path.join(DATA_DIRS, 'input-{}.txt'.format(obj.id)) 19 | 20 | 21 | def get_output_file_path(obj): 22 | return os.path.join(DATA_DIRS, 'output-{}.txt'.format(obj.id)) 23 | 24 | 25 | def get_mail_report_dispatch_file_path(): 26 | return os.path.join(DATA_DIRS, 'mail_report_dispatch.txt') 27 | 28 | 29 | def get_task_cron_comment(obj): 30 | return 'task-{}-pekja'.format(obj.id) 31 | 32 | 33 | def get_batch_task_cron_comment(obj): 34 | return 'batch-task-{}-pekja'.format(obj.id) 35 | 36 | 37 | def get_mail_report_cron_comment(): 38 | return '#send-mail-report-pekja' 39 | 40 | 41 | def get_windows_cron_file_path(): 42 | cron_file_path = os.path.join(DATA_DIRS, 'windows_crontab.txt') 43 | if not os.path.exists(cron_file_path): 44 | with open(cron_file_path, 'w') as f: 45 | pass 46 | return cron_file_path 47 | 48 | 49 | def get_task_by_id(task_id): 50 | """ 51 | 根据ID返回Task 52 | :param task_id: 53 | :return: 54 | """ 55 | try: 56 | task = Task.objects.get(id=task_id) 57 | except Task.DoesNotExist: 58 | return None 59 | else: 60 | return task 61 | 62 | 63 | def get_user_emails(): 64 | """ 65 | 获取所有用户的邮箱地址 66 | :return: 67 | """ 68 | user = get_user_model() 69 | emails = [email for email in user.objects.values_list('email', flat=True) if email != ''] 70 | return emails 71 | 72 | 73 | def send_mail_to_users(subject, title, message): 74 | """ 75 | 给所有用户发送邮件 76 | :param subject: 77 | :param title: 78 | :param message: 79 | :return: 80 | """ 81 | emails = get_user_emails() 82 | if len(emails) > 0: 83 | msg_html = render_to_string('email.html', {'title': title, 'content': message}) 84 | msg = EmailMessage(subject=subject, body=msg_html, from_email=DEFAULT_FROM_EMAIL, to=emails) 85 | msg.content_subtype = 'html' 86 | return msg.send() 87 | 88 | 89 | def rowspan_html_table(headers, input_dict): 90 | """ 91 | 将输入的字典转换为HTML表格 92 | :param headers: 93 | :param input_dict: 94 | :return: 95 | """ 96 | table = '' 97 | for header in headers: 98 | table += '' 99 | table += '' 100 | for key in input_dict: 101 | for index, sub_key in enumerate(input_dict[key]): 102 | row = '' 103 | if index == 0: 104 | row += '' 105 | row += '' 106 | row += '' 107 | row += '' 108 | table += row 109 | table += '
' + escape(header) + '
'.format(len(input_dict[key])) + key + '' + escape(sub_key) + '' + escape(str(input_dict[key][sub_key])) + '
' 110 | return table 111 | 112 | 113 | def open_crontab(): 114 | """ 115 | 打开crontab 116 | :return: 117 | """ 118 | if system() == 'Windows': 119 | cron = CronTab(tabfile=get_windows_cron_file_path()) # 仅用于调试 120 | else: 121 | cron = CronTab(user=CRON_USER) 122 | return cron 123 | 124 | 125 | def human_size(size): 126 | def str_of_size(integer, remainder, level): 127 | if integer >= 1024: 128 | remainder = integer % 1024 129 | integer //= 1024 130 | level += 1 131 | return str_of_size(integer, remainder, level) 132 | else: 133 | return integer, remainder, level 134 | 135 | units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] 136 | integer, remainder, level = str_of_size(size, 0, 0) 137 | if level + 1 > len(units): 138 | level = -1 139 | return '{}.{:>02d} {}'.format(integer, remainder, units[level]) 140 | -------------------------------------------------------------------------------- /entities/resources.py: -------------------------------------------------------------------------------- 1 | from import_export import fields 2 | from import_export import resources 3 | from import_export.widgets import DateTimeWidget 4 | from import_export.widgets import BooleanWidget 5 | from import_export.widgets import ForeignKeyWidget 6 | 7 | from .widgets import ChoicesWidget 8 | from .models import * 9 | 10 | 11 | class ProjectResource(resources.ModelResource): 12 | 13 | id = fields.Field(column_name='ID', attribute='id') 14 | name = fields.Field(column_name='项目名', attribute='name') 15 | src_link = fields.Field(column_name='SRC链接', attribute='src_link') 16 | comment = fields.Field(column_name='备注', attribute='comment') 17 | 18 | class Meta: 19 | model = Project 20 | 21 | 22 | class RecordResource(resources.ModelResource): 23 | 24 | id = fields.Field(column_name='ID', attribute='id') 25 | record = fields.Field(column_name='记录', attribute='record') 26 | project = fields.Field(column_name='项目', attribute='project', widget=ForeignKeyWidget(Project, 'name')) 27 | add_time = fields.Field(column_name='创建时间', 28 | attribute='add_time', 29 | widget=DateTimeWidget('%Y-%m-%d %H:%M:%S')) 30 | last_modify_time = fields.Field(column_name='最后修改时间', 31 | attribute='last_modify_time', 32 | widget=DateTimeWidget('%Y-%m-%d %H:%M:%S')) 33 | type = fields.Field(column_name='类型', attribute='type') 34 | source = fields.Field(column_name='来源', attribute='source') 35 | 36 | class Meta: 37 | model = Record 38 | 39 | 40 | class ToolResource(resources.ModelResource): 41 | 42 | id = fields.Field(column_name='ID', attribute='id') 43 | name = fields.Field(column_name='工具名', attribute='name') 44 | link = fields.Field(column_name='项目地址', attribute='link') 45 | type = fields.Field(column_name='记录类型', attribute='type') 46 | parse_class_name = fields.Field(column_name='解析类名', attribute='parse_class_name') 47 | command = fields.Field(column_name='调用命令', attribute='command') 48 | input_type = fields.Field(column_name='输入参数类型', attribute='input_type', 49 | widget=ChoicesWidget(Tool.input_type_choices)) 50 | version = fields.Field(column_name='版本', attribute='version') 51 | comment = fields.Field(column_name='备注', attribute='comment') 52 | 53 | class Meta: 54 | model = Tool 55 | 56 | 57 | class TaskResource(resources.ModelResource): 58 | 59 | id = fields.Field(column_name='ID', attribute='id') 60 | name = fields.Field(column_name='任务名', attribute='name') 61 | project = fields.Field(column_name='项目', attribute='project', widget=ForeignKeyWidget(Project, 'name')) 62 | tool = fields.Field(column_name='工具', attribute='tool', widget=ForeignKeyWidget(Tool, 'name')) 63 | input = fields.Field(column_name='输入', attribute='input') 64 | dispatch = fields.Field(column_name='调度', attribute='dispatch') 65 | active = fields.Field(column_name='是否生效', attribute='active', widget=BooleanWidget()) 66 | 67 | class Meta: 68 | model = Task 69 | 70 | 71 | class BatchTaskResource(resources.ModelResource): 72 | 73 | id = fields.Field(column_name='ID', attribute='id') 74 | name = fields.Field(column_name='任务名', attribute='name') 75 | task1 = fields.Field(column_name='任务1', attribute='task1', widget=ForeignKeyWidget(Task, 'name')) 76 | task2 = fields.Field(column_name='任务2', attribute='task2', widget=ForeignKeyWidget(Task, 'name')) 77 | task3 = fields.Field(column_name='任务3', attribute='task3', widget=ForeignKeyWidget(Task, 'name')) 78 | task4 = fields.Field(column_name='任务4', attribute='task4', widget=ForeignKeyWidget(Task, 'name')) 79 | task5 = fields.Field(column_name='任务5', attribute='task5', widget=ForeignKeyWidget(Task, 'name')) 80 | task6 = fields.Field(column_name='任务6', attribute='task6', widget=ForeignKeyWidget(Task, 'name')) 81 | task7 = fields.Field(column_name='任务7', attribute='task7', widget=ForeignKeyWidget(Task, 'name')) 82 | task8 = fields.Field(column_name='任务8', attribute='task8', widget=ForeignKeyWidget(Task, 'name')) 83 | task9 = fields.Field(column_name='任务9', attribute='task9', widget=ForeignKeyWidget(Task, 'name')) 84 | task10 = fields.Field(column_name='任务10', attribute='task10', widget=ForeignKeyWidget(Task, 'name')) 85 | dispatch = fields.Field(column_name='调度', attribute='dispatch') 86 | active = fields.Field(column_name='是否生效', attribute='active', widget=BooleanWidget()) 87 | 88 | class Meta: 89 | model = BatchTask 90 | -------------------------------------------------------------------------------- /templates/page/email_report_setting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 邮件报告设置 6 | 7 | 8 | {% load static %} 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
报告接收者
33 |
34 |
35 | 当前邮件报告接收者为: {% for email in emails %}{{ email }}{% if not forloop.last %}、{% endif %}{% endfor %}, 36 | 可前往「认证和授权 › 用户」进行修改。 37 |
38 |
39 |
40 |
41 |
42 |
43 |
报告定时发送
44 |
45 | {% if crontab %} 46 |
{{ crontab }}
47 | {% else %} 48 |
暂无发送邮件报告的定时任务
49 | {% endif %} 50 |
51 |
52 |
53 |
54 |
55 |
设置定时发送
56 |
57 |
58 | {% csrf_token %} 59 |
60 | 61 |
62 | 63 |
64 |
Crontab表达式
65 |
66 |
67 |
68 | 69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | 81 | 82 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /command/cron_task.py: -------------------------------------------------------------------------------- 1 | import os 2 | from sys import executable 3 | 4 | from pekja.utils import get_input_file_path 5 | from pekja.utils import get_output_file_path 6 | from pekja.utils import get_mail_report_dispatch_file_path 7 | from pekja.utils import get_task_cron_comment 8 | from pekja.utils import get_batch_task_cron_comment 9 | from pekja.utils import get_mail_report_cron_comment 10 | from pekja.utils import open_crontab 11 | from pekja.settings import BASE_DIR 12 | from entities.models import Tool 13 | from entities.models import Task 14 | from entities.models import Record 15 | from parse import get_parse_class 16 | 17 | 18 | def set_cron_task(obj): 19 | """ 20 | 设置任务的定时任务 21 | :param obj: Task 模型对象 22 | :return: 23 | """ 24 | command, comment = get_task_command(obj) 25 | set_crontab(obj.dispatch, command, comment, obj.active) 26 | 27 | 28 | def set_cron_batch_task(obj): 29 | """ 30 | 设置批量任务的定时任务 31 | :param obj: 32 | :return: 33 | """ 34 | commands = list() 35 | for task in obj.get_tasks(): 36 | command, _ = get_task_command(task) 37 | commands.append(command) 38 | comment = get_batch_task_cron_comment(obj) 39 | if len(commands) > 0: 40 | set_crontab(obj.dispatch, ' && '.join(commands), comment, obj.active) 41 | 42 | 43 | def get_task_command(task): 44 | """ 45 | 获取任务的命令 46 | :param task: 47 | :return: command, comment 48 | """ 49 | output_file_path = get_output_file_path(task) 50 | task_cron_comment = get_task_cron_comment(task) 51 | 52 | if task.tool.input_type == Tool.INPUT_TYPE_FILE: 53 | if task.input_file_type == Task.INPUT_FILE_TYPE_STATIC: 54 | _input = update_static_input(task) 55 | else: 56 | _input = update_dynamic_input(task) 57 | else: 58 | _input = task.input 59 | 60 | pre_command = '{} {} update_input {}'.format(executable, os.path.join(BASE_DIR, 'manage.py'), task.id) 61 | tool_command = task.tool.command.replace('{input}', _input).replace('{output_file}', output_file_path) 62 | parse_command = '{} {} parse {}'.format(executable, os.path.join(BASE_DIR, 'manage.py'), task.id) 63 | if task.input_file_type == Task.INPUT_FILE_TYPE_DYNAMIC: 64 | command = ' && '.join([pre_command, tool_command, parse_command]) 65 | else: 66 | command = ' && '.join([tool_command, parse_command]) 67 | 68 | return command, task_cron_comment 69 | 70 | 71 | def run_parse(task): 72 | """ 73 | 运行解析类解析任务输出结果 74 | :param task: 75 | :return: 76 | """ 77 | parse_class = get_parse_class(task.tool.parse_class_name) 78 | if parse_class is None: 79 | print('Parse class {} does not exist'.format(task.tool.parse_class_name)) 80 | else: 81 | p = parse_class(task) 82 | p.parse() 83 | p.rename_file() 84 | 85 | 86 | def update_dynamic_input(task): 87 | """ 88 | 更新任务的动态输入 89 | :param task: 90 | :return: 91 | """ 92 | if task.input_file_type == Task.INPUT_FILE_TYPE_DYNAMIC: 93 | input_file_path = get_input_file_path(task) 94 | with open(input_file_path, 'w') as f: 95 | for record in Record.objects.filter(project=task.project, type=task.input): 96 | f.write(record.record) 97 | f.write('\n') 98 | return input_file_path 99 | 100 | 101 | def update_static_input(task): 102 | """ 103 | 更新任务的静态输入 104 | :param task: 105 | :return: 106 | """ 107 | if task.input_file_type == Task.INPUT_FILE_TYPE_STATIC: 108 | input_file_path = get_input_file_path(task) 109 | with open(input_file_path, 'w', newline='') as f: 110 | f.write(task.input) 111 | return input_file_path 112 | 113 | 114 | def set_crontab(dispatch, command, comment, active): 115 | """ 116 | 设置定时任务 117 | :param dispatch: 调度 118 | :param command: 命令 119 | :param comment: 备注,用于查找和更新命令 120 | :param active: 是否生效 121 | :return: 122 | """ 123 | # 打开定时任务文件 124 | cron = open_crontab() 125 | # 删除该任务已存在的定时任务 126 | for job in cron.find_comment(comment): 127 | cron.remove(job) 128 | # 新建定时任务 129 | job = cron.new(command=command, comment=comment) 130 | job.setall(dispatch) 131 | if active: 132 | job.enable() 133 | else: 134 | job.enable(False) 135 | # 设置环境变量 136 | set_crontab_env(cron) 137 | # 保存定时任务 138 | cron.write() 139 | 140 | 141 | def set_crontab_env(cron): 142 | """ 143 | 设置定时任务的环境变量 144 | :param cron 145 | :return: 146 | """ 147 | for key in os.environ: 148 | cron.env[key] = os.environ[key] 149 | 150 | 151 | def set_cron_mail_report(dispatch): 152 | """ 153 | 设置发送邮件报告的定时任务 154 | :param dispatch: 155 | :return: 156 | """ 157 | comment = get_mail_report_cron_comment() 158 | command = '{} {} record_report'.format(executable, os.path.join(BASE_DIR, 'manage.py')) 159 | set_crontab(dispatch, command, comment, True) 160 | with open(get_mail_report_dispatch_file_path(), 'w') as f: 161 | f.write(dispatch) 162 | -------------------------------------------------------------------------------- /parse/tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.test import TestCase 4 | from entities.models import Task 5 | from entities.models import Tool 6 | from entities.models import Record 7 | from entities.models import Project 8 | 9 | from parse.sublist3r_parser import Sublist3rParser 10 | from parse.one_for_all import OneForAllParser 11 | from parse.lijiejie_subdomains_brute import LijiejieSubDomainsBrute 12 | from parse.censys_enumeration_email import CensysEnumerationEmail 13 | from parse.censys_enumeration_domain import CensysEnumerationDomain 14 | from parse.ctfr import CTFRParser 15 | from parse.nmap_dns_brute import NmapDnsBruteParser 16 | from parse.nmap_syn_scan import NmapSynScanParser 17 | from parse.nmap_udp_scan import NmapUdpScanParser 18 | 19 | 20 | class ParserTest(TestCase): 21 | 22 | def setUp(self): 23 | self.project = Project.objects.create(name='test_project') 24 | 25 | def test_sublist3r_parser(self): 26 | tool = Tool.objects.create(name='sublist3r_tool', type='sublist3r_type') 27 | task = Task.objects.create(name='sublist3r_task', project=self.project, tool=tool) 28 | parser = Sublist3rParser(task, os.path.join('parse', 'examples', 'sublist3r.txt')) 29 | parser.parse() 30 | self.assertEqual(Record.objects.filter(type='sublist3r_type').count(), 2) 31 | 32 | def test_censys_enumeration_domain(self): 33 | tool = Tool.objects.create(name='censys_enumeration_domain_tool', type='censys_enumeration_domain_type') 34 | task = Task.objects.create(name='censys_enumeration_domain_task', project=self.project, tool=tool) 35 | parser = CensysEnumerationDomain(task, os.path.join('parse', 'examples', 'censys_enumeration.json')) 36 | parser.parse() 37 | self.assertEqual(Record.objects.filter(type='censys_enumeration_domain_type').count(), 127) 38 | 39 | def test_censys_enumeration_email(self): 40 | tool = Tool.objects.create(name='censys_enumeration_email_tool', type='censys_enumeration_email_type') 41 | task = Task.objects.create(name='censys_enumeration_email_task', project=self.project, tool=tool) 42 | parser = CensysEnumerationEmail(task, os.path.join('parse', 'examples', 'censys_enumeration.json')) 43 | parser.parse() 44 | self.assertEqual(Record.objects.filter(type='censys_enumeration_email_type').count(), 5) 45 | 46 | def test_one_for_all(self): 47 | tool = Tool.objects.create(name='one_for_all_tool', type='one_for_all_type') 48 | task = Task.objects.create(name='one_for_all_task', project=self.project, tool=tool) 49 | parser = OneForAllParser(task, os.path.join('parse', 'examples', 'one_for_all.json')) 50 | parser.parse() 51 | self.assertEqual(Record.objects.filter(type='one_for_all_type').count(), 6) 52 | 53 | def test_lijiejie_sub_domains_brute(self): 54 | tool = Tool.objects.create(name='lijiejie_sub_domains_brute_tool', type='lijiejie_sub_domains_brute_type') 55 | task = Task.objects.create(name='lijiejie_sub_domains_brute_task', project=self.project, tool=tool) 56 | parser = LijiejieSubDomainsBrute(task, os.path.join('parse', 'examples', 'lijiejie_sub_domains_brute.txt')) 57 | parser.parse() 58 | self.assertEqual(Record.objects.filter(type='lijiejie_sub_domains_brute_type').count(), 4) 59 | 60 | def test_ctfr(self): 61 | tool = Tool.objects.create(name='ctfr_tool', type='ctfr_type') 62 | task = Task.objects.create(name='ctfr_task', project=self.project, tool=tool) 63 | parser = CTFRParser(task, os.path.join('parse', 'examples', 'ctfr.txt')) 64 | parser.parse() 65 | self.assertEqual(Record.objects.filter(type='ctfr_type').count(), 7) 66 | 67 | def test_nmap_sub_domain_brute(self): 68 | tool = Tool.objects.create(name='nmap_sub_domain_brute_tool', type='nmap_sub_domain_brute_type') 69 | task = Task.objects.create(name='nmap_sub_domain_brute_task', project=self.project, tool=tool) 70 | parser = NmapDnsBruteParser(task, os.path.join('parse', 'examples', 'nmap_sub_domain_brute.xml')) 71 | parser.parse() 72 | self.assertEqual(Record.objects.filter(type='nmap_sub_domain_brute_type').count(), 4) 73 | 74 | def test_nmap_syn_scan(self): 75 | tool = Tool.objects.create(name='nmap_syn_scan_tool', type='nmap_syn_scan_type') 76 | task = Task.objects.create(name='nmap_syn_scan_task', project=self.project, tool=tool) 77 | parser = NmapSynScanParser(task, os.path.join('parse', 'examples', 'nmap_syn_scan.xml')) 78 | parser.parse() 79 | self.assertEqual(Record.objects.filter(type='nmap_syn_scan_type').count(), 6) 80 | 81 | def test_nmap_udp_scan(self): 82 | tool = Tool.objects.create(name='nmap_udp_scan_tool', type='nmap_udp_scan_type') 83 | task = Task.objects.create(name='nmap_udp_scan_task', project=self.project, tool=tool) 84 | parser = NmapUdpScanParser(task, os.path.join('parse', 'examples', 'nmap_udp_scan.xml')) 85 | parser.parse() 86 | self.assertEqual(Record.objects.filter(type='nmap_udp_scan_type').count(), 2) 87 | 88 | def test_nmap_http_scan(self): 89 | tool = Tool.objects.create(name='nmap_http_scan_tool', type='nmap_http_scan_type') 90 | task = Task.objects.create(name='nmap_http_scan_task', project=self.project, tool=tool) 91 | parser = NmapUdpScanParser(task, os.path.join('parse', 'examples', 'nmap_http_scan.xml')) 92 | parser.parse() 93 | self.assertEqual(Record.objects.filter(type='nmap_http_scan_type').count(), 5) 94 | -------------------------------------------------------------------------------- /templates/page/timeline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 时间线 6 | 7 | 8 | {% load static %} 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 | 时间线 34 |
35 |
    36 | {% for record in records %} 37 |
  • 38 | 39 |
    40 |

    {{ record.date }}

    41 | {% for content in record.contents %} 42 |

    43 | {{ content.record }} 44 | {{content.project}} 45 | {% if content.type %} 46 | {{content.type}} 47 | {% endif %} 48 | {% if content.source %} 49 | {{content.source}} 50 | {% endif %} 51 |

    52 | {% endfor %} 53 |
    54 |
  • 55 | {% endfor %} 56 |
  • 57 | 58 |
    59 |
    开始
    60 |
    61 |
  • 62 |
63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 |
73 | 74 |
75 |
76 | 过滤 77 |
78 |
79 |
80 | 81 |
82 | 83 |
84 |
85 |
86 | 87 |
88 | 89 |
90 |
91 |
92 |
93 | 94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | 104 | 105 | -------------------------------------------------------------------------------- /entities/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from .validators import command_validator 3 | from .validators import cron_validator 4 | 5 | 6 | class Project(models.Model): 7 | 8 | name = models.CharField(verbose_name='项目名', max_length=100, unique=True) 9 | src_link = models.URLField(verbose_name='SRC链接', null=True, blank=True) 10 | comment = models.TextField(verbose_name='备注', null=True, blank=True) 11 | 12 | def __str__(self): 13 | return self.name 14 | 15 | class Meta: 16 | verbose_name = '项目' 17 | verbose_name_plural = verbose_name 18 | 19 | 20 | class Record(models.Model): 21 | 22 | record = models.CharField(verbose_name='记录', max_length=200) 23 | project = models.ForeignKey(verbose_name='项目', to=Project, on_delete=models.CASCADE) 24 | add_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 25 | last_modify_time = models.DateTimeField(verbose_name='最后修改时间', auto_now=True) 26 | type = models.CharField(verbose_name='类型', max_length=50, null=True, blank=True) 27 | source = models.CharField(verbose_name='来源', max_length=50, null=True, blank=True) 28 | 29 | def __str__(self): 30 | return self.record 31 | 32 | class Meta: 33 | verbose_name = '记录' 34 | verbose_name_plural = verbose_name 35 | 36 | 37 | class Tool(models.Model): 38 | 39 | INPUT_TYPE_FILE = 'file' 40 | INPUT_TYPE_PARAMETER = 'parameter' 41 | input_type_choices = ((INPUT_TYPE_FILE, '文件'), 42 | (INPUT_TYPE_PARAMETER, '参数')) 43 | 44 | name = models.CharField(verbose_name='工具名', max_length=100, unique=True) 45 | link = models.URLField(verbose_name='项目地址', null=True, blank=True) 46 | type = models.CharField(verbose_name='记录类型', max_length=50) 47 | parse_class_name = models.CharField(verbose_name='输出解析类', max_length=50) 48 | command = models.CharField(verbose_name='调用命令', max_length=500, validators=[command_validator]) 49 | input_type = models.CharField(verbose_name='输入参数类型', max_length=50, 50 | choices=input_type_choices, default=INPUT_TYPE_FILE) 51 | version = models.CharField(verbose_name='版本', max_length=50, null=True, blank=True) 52 | comment = models.CharField(verbose_name='备注', null=True, blank=True, max_length=100) 53 | 54 | def __str__(self): 55 | return self.name 56 | 57 | class Meta: 58 | verbose_name = '工具' 59 | verbose_name_plural = verbose_name 60 | 61 | 62 | class Task(models.Model): 63 | 64 | INPUT_FILE_TYPE_STATIC = 'static_file' 65 | INPUT_FILE_TYPE_DYNAMIC = 'dynamic_file' 66 | input_file_type_choices = ((INPUT_FILE_TYPE_STATIC, '静态'), 67 | (INPUT_FILE_TYPE_DYNAMIC, '动态')) 68 | name = models.CharField(verbose_name='任务名', max_length=100, unique=True) 69 | project = models.ForeignKey(verbose_name='所属项目', to=Project, on_delete=models.CASCADE) 70 | tool = models.ForeignKey(verbose_name='工具', to=Tool, on_delete=models.CASCADE) 71 | input = models.TextField(verbose_name='输入') 72 | input_file_type = models.CharField(verbose_name='输入文件类型', max_length=50, 73 | choices=input_file_type_choices, default=INPUT_FILE_TYPE_STATIC) 74 | dispatch = models.CharField(verbose_name='调度', max_length=100, validators=[cron_validator]) 75 | active = models.BooleanField(verbose_name='是否生效', default=True) 76 | 77 | def __str__(self): 78 | return '{}-{}'.format(self.project.name, self.name) 79 | 80 | class Meta: 81 | verbose_name = '任务' 82 | verbose_name_plural = verbose_name 83 | 84 | 85 | class BatchTask(models.Model): 86 | 87 | name = models.CharField(verbose_name='批量任务名', max_length=100, unique=True) 88 | task1 = models.ForeignKey(verbose_name='任务1', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task1') 89 | task2 = models.ForeignKey(verbose_name='任务2', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task2') 90 | task3 = models.ForeignKey(verbose_name='任务3', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task3') 91 | task4 = models.ForeignKey(verbose_name='任务4', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task4') 92 | task5 = models.ForeignKey(verbose_name='任务5', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task5') 93 | task6 = models.ForeignKey(verbose_name='任务6', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task6') 94 | task7 = models.ForeignKey(verbose_name='任务7', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task7') 95 | task8 = models.ForeignKey(verbose_name='任务8', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task8') 96 | task9 = models.ForeignKey(verbose_name='任务9', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task9') 97 | task10 = models.ForeignKey(verbose_name='任务10', to=Task, on_delete=models.SET_NULL, null=True, blank=True, related_name='task10') 98 | dispatch = models.CharField(verbose_name='调度', max_length=100, validators=[cron_validator]) 99 | active = models.BooleanField(verbose_name='是否生效', default=True) 100 | 101 | MAX_TASK_AMOUNT = 10 102 | 103 | def get_tasks(self): 104 | tasks = list() 105 | for i in range(1, self.MAX_TASK_AMOUNT+1): 106 | if getattr(self, 'task{}'.format(i)) is not None: 107 | tasks.append(getattr(self, 'task{}'.format(i))) 108 | return tasks 109 | 110 | def __str__(self): 111 | return self.name 112 | 113 | class Meta: 114 | verbose_name = '批量任务' 115 | verbose_name_plural = verbose_name 116 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/transfer.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define(["laytpl","form"],function(e){"use strict";var a=layui.$,t=layui.laytpl,n=layui.form,i="transfer",l={config:{},index:layui[i]?layui[i].index+1e4:0,set:function(e){var t=this;return t.config=a.extend({},t.config,e),t},on:function(e,a){return layui.onevent.call(this,i,e,a)}},r=function(){var e=this,a=e.config,t=a.id||e.index;return r.that[t]=e,r.config[t]=a,{config:a,reload:function(a){e.reload.call(e,a)},getData:function(){return e.getData.call(e)}}},c="layui-hide",o="layui-btn-disabled",d="layui-none",s="layui-transfer-box",u="layui-transfer-header",h="layui-transfer-search",f="layui-transfer-active",y="layui-transfer-data",p=function(e){return e=e||{},['
','
','","
","{{# if(d.data.showSearch){ }}",'","{{# } }}",'
    ',"
    "].join("")},v=['
    ',p({index:0,checkAllName:"layTransferLeftCheckAll"}),'
    ','",'","
    ",p({index:1,checkAllName:"layTransferRightCheckAll"}),"
    "].join(""),x=function(e){var t=this;t.index=++l.index,t.config=a.extend({},t.config,l.config,e),t.render()};x.prototype.config={title:["列表一","列表二"],width:200,height:360,data:[],value:[],showSearch:!1,id:"",text:{none:"无数据",searchNone:"无匹配数据"}},x.prototype.reload=function(e){var t=this;layui.each(e,function(e,a){a.constructor===Array&&delete t.config[e]}),t.config=a.extend(!0,{},t.config,e),t.render()},x.prototype.render=function(){var e=this,n=e.config,i=e.elem=a(t(v).render({data:n,index:e.index})),l=n.elem=a(n.elem);l[0]&&(n.data=n.data||[],n.value=n.value||[],e.key=n.id||e.index,l.html(e.elem),e.layBox=e.elem.find("."+s),e.layHeader=e.elem.find("."+u),e.laySearch=e.elem.find("."+h),e.layData=i.find("."+y),e.layBtn=i.find("."+f+" .layui-btn"),e.layBox.css({width:n.width,height:n.height}),e.layData.css({height:function(){return n.height-e.layHeader.outerHeight()-e.laySearch.outerHeight()-2}()}),e.renderData(),e.events())},x.prototype.renderData=function(){var e=this,a=(e.config,[{checkName:"layTransferLeftCheck",views:[]},{checkName:"layTransferRightCheck",views:[]}]);e.parseData(function(e){var t=e.selected?1:0,n=["
  • ",'',"
  • "].join("");a[t].views.push(n),delete e.selected}),e.layData.eq(0).html(a[0].views.join("")),e.layData.eq(1).html(a[1].views.join("")),e.renderCheckBtn()},x.prototype.renderForm=function(e){n.render(e,"LAY-transfer-"+this.index)},x.prototype.renderCheckBtn=function(e){var t=this,n=t.config;e=e||{},t.layBox.each(function(i){var l=a(this),r=l.find("."+y),d=l.find("."+u).find('input[type="checkbox"]'),s=r.find('input[type="checkbox"]'),h=0,f=!1;if(s.each(function(){var e=a(this).data("hide");(this.checked||this.disabled||e)&&h++,this.checked&&!e&&(f=!0)}),d.prop("checked",f&&h===s.length),t.layBtn.eq(i)[f?"removeClass":"addClass"](o),!e.stopNone){var p=r.children("li:not(."+c+")").length;t.noneView(r,p?"":n.text.none)}}),t.renderForm("checkbox")},x.prototype.noneView=function(e,t){var n=a('

    '+(t||"")+"

    ");e.find("."+d)[0]&&e.find("."+d).remove(),t.replace(/\s/g,"")&&e.append(n)},x.prototype.setValue=function(){var e=this,t=e.config,n=[];return e.layBox.eq(1).find("."+y+' input[type="checkbox"]').each(function(){var e=a(this).data("hide");e||n.push(this.value)}),t.value=n,e},x.prototype.parseData=function(e){var t=this,n=t.config,i=[];return layui.each(n.data,function(t,l){l=("function"==typeof n.parseData?n.parseData(l):l)||l,i.push(l=a.extend({},l)),layui.each(n.value,function(e,a){a==l.value&&(l.selected=!0)}),e&&e(l)}),n.data=i,t},x.prototype.getData=function(e){var a=this,t=a.config,n=[];return a.setValue(),layui.each(e||t.value,function(e,a){layui.each(t.data,function(e,t){delete t.selected,a==t.value&&n.push(t)})}),n},x.prototype.events=function(){var e=this,t=e.config;e.elem.on("click",'input[lay-filter="layTransferCheckbox"]+',function(){var t=a(this).prev(),n=t[0].checked,i=t.parents("."+s).eq(0).find("."+y);t[0].disabled||("all"===t.attr("lay-type")&&i.find('input[type="checkbox"]').each(function(){this.disabled||(this.checked=n)}),e.renderCheckBtn({stopNone:!0}))}),e.layBtn.on("click",function(){var n=a(this),i=n.data("index"),l=e.layBox.eq(i),r=[];if(!n.hasClass(o)){e.layBox.eq(i).each(function(t){var n=a(this),i=n.find("."+y);i.children("li").each(function(){var t=a(this),n=t.find('input[type="checkbox"]'),i=n.data("hide");n[0].checked&&!i&&(n[0].checked=!1,l.siblings("."+s).find("."+y).append(t.clone()),t.remove(),r.push(n[0].value)),e.setValue()})}),e.renderCheckBtn();var c=l.siblings("."+s).find("."+h+" input");""===c.val()||c.trigger("keyup"),t.onchange&&t.onchange(e.getData(r),i)}}),e.laySearch.find("input").on("keyup",function(){var n=this.value,i=a(this).parents("."+h).eq(0).siblings("."+y),l=i.children("li");l.each(function(){var e=a(this),t=e.find('input[type="checkbox"]'),i=t[0].title.indexOf(n)!==-1;e[i?"removeClass":"addClass"](c),t.data("hide",!i)}),e.renderCheckBtn();var r=l.length===i.children("li."+c).length;e.noneView(i,r?t.text.searchNone:"")})},r.that={},r.config={},l.reload=function(e,a){var t=r.that[e];return t.reload(a),r.call(t)},l.getData=function(e){var a=r.that[e];return a.getData()},l.render=function(e){var a=new x(e);return r.call(a)},e(i,l)}); -------------------------------------------------------------------------------- /parse/examples/censys_enumeration.json: -------------------------------------------------------------------------------- 1 | { 2 | "ietf.org": { 3 | "domain": "ietf.org", 4 | "emails": [ 5 | "randy@psg.com", 6 | "noc@meeting.ietf.org", 7 | "webmaster@machshav.com", 8 | "root@dechaunac.tools.ietf.org" 9 | ], 10 | "subdomains": [ 11 | "www.rtg.ietf.org", 12 | "netdot.noc.ietf.org", 13 | "services-1.meeting.ietf.org", 14 | "permatrac.noc.ietf.org", 15 | "nms.sql1.ietf.org", 16 | "sql1.ietf.org", 17 | "etherpad.noc.ietf.org", 18 | "permatrac.sql1.ietf.org", 19 | "tools.ietf.org", 20 | "observium.meeting.ietf.org", 21 | "Switch.meeting.ietf.org", 22 | "sec.ietf.org", 23 | "ops.ietf.org", 24 | "dechaunac.tools.ietf.org", 25 | "vm1.noc.ietf.org", 26 | "codestand.ietf.org", 27 | "git.noc.ietf.org", 28 | "tickets.meeting.ietf.org", 29 | "noc.meeting.ietf.org", 30 | "vm0.noc.ietf.org", 31 | "noc.ietf.org", 32 | "etherpad.sql1.ietf.org", 33 | "meeting.ietf.org", 34 | "management.meeting.ietf.org", 35 | "rtg.ietf.org", 36 | "netdot.sql1.ietf.org", 37 | "ietf.org", 38 | "nms.noc.ietf.org", 39 | "services-2.meeting.ietf.org", 40 | "ztp.meeting.ietf.org", 41 | "deadperson.meeting.ietf.org", 42 | "vnam.meeting.ietf.org", 43 | "dhcp-8599.meeting.ietf.org", 44 | "vm2.noc.ietf.org", 45 | "dav.noc.ietf.org" 46 | ] 47 | }, 48 | "censys.io": { 49 | "domain": "censys.io", 50 | "emails": [ 51 | "hostmaster@censys.io" 52 | ], 53 | "subdomains": [ 54 | "kibana2.censys.io", 55 | "www.censys.io", 56 | "es-frontend-1.censys.io", 57 | "elasticsearch2.censys.io", 58 | "kafka-1.censys.io", 59 | "alertmanager.censys.io", 60 | "kafka2.censys.io", 61 | "kafka1.censys.io", 62 | "elasticsearch1.censys.io", 63 | "kibana1.censys.io", 64 | "git.wiki.censys.io", 65 | "censys.io", 66 | "logs.censys.io", 67 | "prometheus.censys.io", 68 | "wiki.censys.io", 69 | "kibana.es-frontend-1.censys.io", 70 | "kafka3.censys.io", 71 | "grafana.censys.io", 72 | "support.censys.io", 73 | "ichnaea.censys.io", 74 | "kibana.es-frontend-2.censys.io", 75 | "es-frontend-2.censys.io" 76 | ] 77 | }, 78 | "wikimedia.org": { 79 | "domain": "wikimedia.org", 80 | "emails": [ 81 | 82 | ], 83 | "subdomains": [ 84 | "apt.wikimedia.org", 85 | "etherpad.wikimedia.org", 86 | "status.wikimedia.org", 87 | "blog.wikimedia.org", 88 | "bug-attachment.wikimedia.org", 89 | "mirrors.wikimedia.org", 90 | "mail.wikimedia.org", 91 | "ticket.wikimedia.org", 92 | "vpn.corp.wikimedia.org", 93 | "ganglia.wikimedia.org", 94 | "wikitech.wikimedia.org", 95 | "netbox.wikimedia.org", 96 | "mx1001.wikimedia.org", 97 | "icinga-admin.wikimedia.org", 98 | "frdev.wikimedia.org", 99 | "archiva.wikimedia.org", 100 | "civicrm.wikimedia.org", 101 | "frdata.wikimedia.org", 102 | "noc.wikimedia.org", 103 | "gerrit-slave.wikimedia.org", 104 | "ishmael.wikimedia.org", 105 | "git.wikimedia.org", 106 | "rt.wikimedia.org", 107 | "dumps.wikimedia.org", 108 | "tendril.wikimedia.org", 109 | "svn.wikimedia.org", 110 | "corp.wikimedia.org", 111 | "stats.wikimedia.org", 112 | "virt0.wikimedia.org", 113 | "metrics.wikimedia.org", 114 | "fundraising.wikimedia.org", 115 | "wikitech-static-iad.wikimedia.org", 116 | "policy.wikimedia.org", 117 | "download.wikimedia.org", 118 | "ubuntu.wikimedia.org", 119 | "wikitech-static.wikimedia.org", 120 | "wikitech-static-ord.wikimedia.org", 121 | "directory.corp.wikimedia.org", 122 | "racktables.wikimedia.org", 123 | "mx2002.wikimedia.org", 124 | "mx1002.wikimedia.org", 125 | "shop.wikimedia.org", 126 | "payments.wikimedia.org", 127 | "benefactorevents.wikimedia.org", 128 | "techblog.wikimedia.org", 129 | "mingle.corp.wikimedia.org", 130 | "lists.wikimedia.org", 131 | "sugar.corp.wikimedia.org", 132 | "gerrit-new.wikimedia.org", 133 | "bugzilla.wikimedia.org", 134 | "eventdonations.wikimedia.org", 135 | "planet.wikimedia.org", 136 | "stream.wikimedia.org", 137 | "www.ticket.wikimedia.org", 138 | "store.wikimedia.org", 139 | "www.policy.wikimedia.org", 140 | "librenms.wikimedia.org", 141 | "payments-listener.wikimedia.org", 142 | "icinga.wikimedia.org", 143 | "labtestwikitech.wikimedia.org", 144 | "gerrit.wikimedia.org", 145 | "mx2001.wikimedia.org", 146 | "m.wikimedia.org", 147 | "wikimedia.org" 148 | ] 149 | }, 150 | "iana.org": { 151 | "domain": "iana.org", 152 | "emails": [ 153 | 154 | ], 155 | "subdomains": [ 156 | "www.itar.iana.org", 157 | "data.iana.org", 158 | "iana.org", 159 | "www.ns.iana.org", 160 | "ns.iana.org", 161 | "itar.iana.org" 162 | ] 163 | } 164 | } -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/layui.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;!function(e){"use strict";var t=document,o={modules:{},status:{},timeout:10,event:{}},n=function(){this.v="2.5.5"},r=function(){var e=t.currentScript?t.currentScript.src:function(){for(var e,o=t.scripts,n=o.length-1,r=n;r>0;r--)if("interactive"===o[r].readyState){e=o[r].src;break}return e||o[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),i=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},a="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",transfer:"modules/transfer",tree:"modules/tree",table:"modules/table",element:"modules/element",rate:"modules/rate",colorpicker:"modules/colorpicker",slider:"modules/slider",carousel:"modules/carousel",flow:"modules/flow",util:"modules/util",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};n.prototype.cache=o,n.prototype.define=function(e,t){var n=this,r="function"==typeof e,i=function(){var e=function(e,t){layui[e]=t,o.status[e]=!0};return"function"==typeof t&&t(function(n,r){e(n,r),o.callback[n]=function(){t(e)}}),this};return r&&(t=e,e=[]),!layui["layui.all"]&&layui["layui.mobile"]?i.call(n):(n.use(e,i),n)},n.prototype.use=function(e,n,l){function s(e,t){var n="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||n.test((e.currentTarget||e.srcElement).readyState))&&(o.modules[f]=t,d.removeChild(v),function r(){return++m>1e3*o.timeout/4?i(f+" is not a valid module"):void(o.status[f]?c():setTimeout(r,4))}())}function c(){l.push(layui[f]),e.length>1?y.use(e.slice(1),n,l):"function"==typeof n&&n.apply(layui,l)}var y=this,p=o.dir=o.dir?o.dir:r,d=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,o){"jquery"===o&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var f=e[0],m=0;if(l=l||[],o.host=o.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[f]||!layui["layui.all"]&&layui["layui.mobile"]&&u[f])return c(),y;if(o.modules[f])!function g(){return++m>1e3*o.timeout/4?i(f+" is not a valid module"):void("string"==typeof o.modules[f]&&o.status[f]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[f]?p+"lay/":/^\{\/\}/.test(y.modules[f])?"":o.base||"")+(y.modules[f]||f)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=o.version===!0?o.v||(new Date).getTime():o.version||"";return e?"?v="+e:""}(),d.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||a?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),o.modules[f]=h}return y},n.prototype.getStyle=function(t,o){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](o)},n.prototype.link=function(e,n,r){var a=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof n&&(r=n);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(o.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof n?a:(function p(){return++y>1e3*o.timeout/100?i(e+" timeout"):void(1989===parseInt(a.getStyle(t.getElementById(c),"width"))?function(){n()}():setTimeout(p,100))}(),a)},o.callback={},n.prototype.factory=function(e){if(layui[e])return"function"==typeof o.callback[e]?o.callback[e]:null},n.prototype.addcss=function(e,t,n){return layui.link(o.dir+"css/"+e,t,n)},n.prototype.img=function(e,t,o){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,"function"==typeof t&&t(n)},void(n.onerror=function(e){n.onerror=null,"function"==typeof o&&o(e)}))},n.prototype.config=function(e){e=e||{};for(var t in e)o[t]=e[t];return this},n.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),n.prototype.extend=function(e){var t=this;e=e||{};for(var o in e)t[o]||t.modules[o]?i("模块名 "+o+" 已被占用"):t.modules[o]=e[o];return t},n.prototype.router=function(e){var t=this,e=e||location.hash,o={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),o.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),o.search[t[0]]=t[1]}():o.path.push(t)}),o):o},n.prototype.data=function(t,o,n){if(t=t||"layui",n=n||localStorage,e.JSON&&e.JSON.parse){if(null===o)return delete n[t];o="object"==typeof o?o:{key:o};try{var r=JSON.parse(n[t])}catch(i){var r={}}return"value"in o&&(r[o.key]=o.value),o.remove&&delete r[o.key],n[t]=JSON.stringify(r),o.key?r[o.key]:r}},n.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},n.prototype.device=function(t){var o=navigator.userAgent.toLowerCase(),n=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(o.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(o)?"windows":/linux/.test(o)?"linux":/iphone|ipod|ipad|ios/.test(o)?"ios":/mac/.test(o)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((o.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:n("micromessenger")};return t&&!r[t]&&(r[t]=n(t)),r.android=/android/.test(o),r.ios="ios"===r.os,r},n.prototype.hint=function(){return{error:i}},n.prototype.each=function(e,t){var o,n=this;if("function"!=typeof t)return n;if(e=e||[],e.constructor===Object){for(o in e)if(t.call(e[o],o,e[o]))break}else for(o=0;oi?1:rt.min?a:t.min,t.value[1]=n>t.min?n:t.min,t.value[0]=t.value[0]>t.max?t.max:t.value[0],t.value[1]=t.value[1]>t.max?t.max:t.value[1];var r=Math.floor((t.value[0]-t.min)/(t.max-t.min)*100),v=Math.floor((t.value[1]-t.min)/(t.max-t.min)*100),m=v-r+"%";r+="%",v+="%"}else{"object"==typeof t.value&&(t.value=Math.min.apply(null,t.value)),t.valuet.max&&(t.value=t.max);var m=Math.floor((t.value-t.min)/(t.max-t.min)*100)+"%"}var p=t.disabled?"#c2c2c2":t.theme,f='
    '+(t.tips?'
    ':"")+'
    '+(t.range?'
    ':"")+"
    ",h=i(t.elem),y=h.next("."+s);if(y[0]&&y.remove(),e.elemTemp=i(f),t.range?(e.elemTemp.find("."+o).eq(0).data("value",t.value[0]),e.elemTemp.find("."+o).eq(1).data("value",t.value[1])):e.elemTemp.find("."+o).data("value",t.value),h.html(e.elemTemp),"vertical"===t.type&&e.elemTemp.height(t.height+"px"),t.showstep){for(var g=(t.max-t.min)/t.step,b="",x=1;x')}e.elemTemp.append(b)}if(t.input&&!t.range){var w=i('
    ');h.css("position","relative"),h.append(w),h.find("."+c).children("input").val(t.value),"vertical"===t.type?w.css({left:0,top:-48}):e.elemTemp.css("margin-right",w.outerWidth()+15)}t.disabled?(e.elemTemp.addClass(l),e.elemTemp.find("."+u).addClass(l)):e.slide(),e.elemTemp.find("."+u).on("mouseover",function(){var a="vertical"===t.type?t.height:e.elemTemp[0].offsetWidth,n=e.elemTemp.find("."+o),l="vertical"===t.type?a-i(this).parent()[0].offsetTop-n.height():i(this).parent()[0].offsetLeft,s=l/a*100,r=i(this).parent().data("value"),u=t.setTips?t.setTips(r):r;e.elemTemp.find("."+d).html(u),"vertical"===t.type?e.elemTemp.find("."+d).css({bottom:s+"%","margin-bottom":"20px",display:"inline-block"}):e.elemTemp.find("."+d).css({left:s+"%",display:"inline-block"})}).on("mouseout",function(){e.elemTemp.find("."+d).css("display","none")})},f.prototype.slide=function(e,t,a){var n=this,l=n.config,s=n.elemTemp,f=function(){return"vertical"===l.type?l.height:s[0].offsetWidth},h=s.find("."+o),y=s.next("."+v),g=y.children("."+c).children("input").val(),b=100/((l.max-l.min)/Math.ceil(l.step)),x=function(e,i){e=Math.ceil(e)*b>100?Math.ceil(e)*b:Math.round(e)*b,e=e>100?100:e,h.eq(i).css("vertical"===l.type?"bottom":"left",e+"%");var t=T(h[0].offsetLeft),a=l.range?T(h[1].offsetLeft):0;"vertical"===l.type?(s.find("."+d).css({bottom:e+"%","margin-bottom":"20px"}),t=T(f()-h[0].offsetTop-h.height()),a=l.range?T(f()-h[1].offsetTop-h.height()):0):s.find("."+d).css("left",e+"%"),t=t>100?100:t,a=a>100?100:a;var n=Math.min(t,a),o=Math.abs(t-a);"vertical"===l.type?s.find("."+r).css({height:o+"%",bottom:n+"%"}):s.find("."+r).css({width:o+"%",left:n+"%"});var u=l.min+Math.round((l.max-l.min)*e/100);if(g=u,y.children("."+c).children("input").val(g),h.eq(i).data("value",u),u=l.setTips?l.setTips(u):u,s.find("."+d).html(u),l.range){var v=[h.eq(0).data("value"),h.eq(1).data("value")];v[0]>v[1]&&v.reverse()}l.change&&l.change(l.range?v:u)},T=function(e){var i=e/f()*100/b,t=Math.round(i)*b;return e==f()&&(t=Math.ceil(i)*b),t},w=i(['
    f()&&(r=f());var o=r/f()*100/b;x(o,e),t.addClass(p),s.find("."+d).show(),i.preventDefault()},o=function(){t.removeClass(p),s.find("."+d).hide()};M(r,o)})}),s.on("click",function(e){var t=i("."+u);if(!t.is(event.target)&&0===t.has(event.target).length&&t.length){var a,n="vertical"===l.type?f()-e.clientY+i(this).offset().top:e.clientX-i(this).offset().left;n<0&&(n=0),n>f()&&(n=f());var s=n/f()*100/b;a=l.range?"vertical"===l.type?Math.abs(n-parseInt(i(h[0]).css("bottom")))>Math.abs(n-parseInt(i(h[1]).css("bottom")))?1:0:Math.abs(n-h[0].offsetLeft)>Math.abs(n-h[1].offsetLeft)?1:0:0,x(s,a),e.preventDefault()}}),y.hover(function(){var e=i(this);e.children("."+m).fadeIn("fast")},function(){var e=i(this);e.children("."+m).fadeOut("fast")}),y.children("."+m).children("i").each(function(e){i(this).on("click",function(){g=1==e?g-l.stepl.max?l.max:Number(g)+l.step;var i=(g-l.min)/(l.max-l.min)*100/b;x(i,0)})});var q=function(){var e=this.value;e=isNaN(e)?0:e,e=el.max?l.max:e,this.value=e;var i=(e-l.min)/(l.max-l.min)*100/b;x(i,0)};y.children("."+c).children("input").on("keydown",function(e){13===e.keyCode&&(e.preventDefault(),q.call(this))}).on("change",q)},f.prototype.events=function(){var e=this;e.config},t.render=function(e){var i=new f(e);return a.call(i)},e(n,t)}); -------------------------------------------------------------------------------- /entities/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-06 02:08 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | import entities.validators 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Project', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('name', models.CharField(max_length=100, unique=True, verbose_name='项目名')), 21 | ('src_link', models.URLField(blank=True, null=True, verbose_name='SRC链接')), 22 | ('comment', models.TextField(blank=True, null=True, verbose_name='备注')), 23 | ], 24 | options={ 25 | 'verbose_name': '项目', 26 | 'verbose_name_plural': '项目', 27 | }, 28 | ), 29 | migrations.CreateModel( 30 | name='Tool', 31 | fields=[ 32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 33 | ('name', models.CharField(max_length=100, unique=True, verbose_name='工具名')), 34 | ('link', models.URLField(blank=True, null=True, verbose_name='项目地址')), 35 | ('type', models.CharField(max_length=50, verbose_name='记录类型')), 36 | ('parse_class_name', models.CharField(max_length=50, verbose_name='输出解析类')), 37 | ('command', models.CharField(max_length=500, validators=[entities.validators.command_validator], verbose_name='调用命令')), 38 | ('input_type', models.CharField(choices=[('file', '文件'), ('parameter', '参数')], default='file', max_length=50, verbose_name='输入参数类型')), 39 | ('version', models.CharField(blank=True, max_length=50, null=True, verbose_name='版本')), 40 | ('comment', models.CharField(blank=True, max_length=100, null=True, verbose_name='备注')), 41 | ], 42 | options={ 43 | 'verbose_name': '工具表', 44 | 'verbose_name_plural': '工具表', 45 | }, 46 | ), 47 | migrations.CreateModel( 48 | name='Task', 49 | fields=[ 50 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 51 | ('name', models.CharField(max_length=100, unique=True, verbose_name='任务名')), 52 | ('input', models.TextField(verbose_name='输入')), 53 | ('input_file_type', models.CharField(choices=[('static_file', '静态'), ('dynamic_file', '动态')], default='static_file', max_length=50, verbose_name='输入文件类型')), 54 | ('dispatch', models.CharField(max_length=100, validators=[entities.validators.cron_validator], verbose_name='调度')), 55 | ('active', models.BooleanField(default=True, verbose_name='是否生效')), 56 | ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.Project', verbose_name='所属项目')), 57 | ('tool', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.Tool', verbose_name='工具')), 58 | ], 59 | options={ 60 | 'verbose_name': '任务表', 61 | 'verbose_name_plural': '任务表', 62 | }, 63 | ), 64 | migrations.CreateModel( 65 | name='Record', 66 | fields=[ 67 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 68 | ('record', models.CharField(max_length=200, verbose_name='记录')), 69 | ('add_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 70 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='最后修改时间')), 71 | ('type', models.CharField(blank=True, max_length=50, null=True, verbose_name='类型')), 72 | ('source', models.CharField(blank=True, max_length=50, null=True, verbose_name='来源')), 73 | ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.Project', verbose_name='项目')), 74 | ], 75 | options={ 76 | 'verbose_name': '记录', 77 | 'verbose_name_plural': '记录', 78 | }, 79 | ), 80 | migrations.CreateModel( 81 | name='BatchTask', 82 | fields=[ 83 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 84 | ('name', models.CharField(max_length=100, unique=True, verbose_name='批量任务名')), 85 | ('dispatch', models.CharField(max_length=100, validators=[entities.validators.cron_validator], verbose_name='调度')), 86 | ('active', models.BooleanField(default=True, verbose_name='是否生效')), 87 | ('task1', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task1', to='entities.Task', verbose_name='任务1')), 88 | ('task10', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task10', to='entities.Task', verbose_name='任务10')), 89 | ('task2', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task2', to='entities.Task', verbose_name='任务2')), 90 | ('task3', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task3', to='entities.Task', verbose_name='任务3')), 91 | ('task4', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task4', to='entities.Task', verbose_name='任务4')), 92 | ('task5', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task5', to='entities.Task', verbose_name='任务5')), 93 | ('task6', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task6', to='entities.Task', verbose_name='任务6')), 94 | ('task7', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task7', to='entities.Task', verbose_name='任务7')), 95 | ('task8', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task8', to='entities.Task', verbose_name='任务8')), 96 | ('task9', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task9', to='entities.Task', verbose_name='任务9')), 97 | ], 98 | options={ 99 | 'verbose_name': '批量任务表', 100 | 'verbose_name_plural': '批量任务表', 101 | }, 102 | ), 103 | ] 104 | -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/upload.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,n=layui.hint(),o=layui.device(),a={config:{},set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,r,e,t)}},l=function(){var e=this;return{upload:function(t){e.upload.call(e,t)},reload:function(t){e.reload.call(e,t)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var i=this;i.config=t.extend({},i.config,a.config,e),i.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",acceptMime:"",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var i=this,e=i.config;e.elem=t(e.elem),e.bindAction=t(e.bindAction),i.file(),i.events()},p.prototype.file=function(){var e=this,i=e.config,n=e.elemFile=t(['"].join("")),a=i.elem.next();(a.hasClass(u)||a.hasClass(c))&&a.remove(),o.ie&&o.ie<10&&i.elem.wrap('
    '),e.isFile()?(e.elemFile=i.elem,i.field=i.elem[0].name):i.elem.after(n),o.ie&&o.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,i=e.config,n=t(''),o=t(['
    ',"
    "].join(""));t("#"+f)[0]||t("body").append(n),i.elem.next().hasClass(c)||(e.elemFile.wrap(o),i.elem.next("."+c).append(function(){var e=[];return layui.each(i.data,function(t,i){i="function"==typeof i?i():i,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return i.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var t=this;window.FileReader&&layui.each(t.chooseFiles,function(t,i){var n=new FileReader;n.readAsDataURL(i),n.onload=function(){e&&e(t,i,this.result)}})},p.prototype.upload=function(e,i){var n,a=this,l=a.config,r=a.elemFile[0],u=function(){var i=0,n=0,o=e||a.files||a.chooseFiles||r.files,u=function(){l.multiple&&i+n===a.fileLength&&"function"==typeof l.allDone&&l.allDone({total:a.fileLength,successful:i,aborted:n})};layui.each(o,function(e,o){var r=new FormData;r.append(l.field,o),layui.each(l.data,function(e,t){t="function"==typeof t?t():t,r.append(e,t)}),t.ajax({url:l.url,type:"post",data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(t){i++,d(e,t),u()},error:function(){n++,a.msg("请求上传接口出现异常"),m(e),u()},xhr:function(){var e=new XMLHttpRequest;return e.upload.addEventListener("progress",function(e){if(e.lengthComputable){var t=Math.floor(e.loaded/e.total*100);"function"==typeof l.progress&&l.progress(t,e)}}),e}})})},c=function(){var e=t("#"+f);a.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var t,i=e.contents().find("body");try{t=i.text()}catch(n){a.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}t&&(clearInterval(p.timer),i.html(""),d(0,t))},30)},d=function(e,t){if(a.elemFile.next("."+s).remove(),r.value="","object"!=typeof t)try{t=JSON.parse(t)}catch(i){return t={},a.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(t,e||0,function(e){a.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){a.upload(e)})},h=l.exts,v=function(){var t=[];return layui.each(e||a.chooseFiles,function(e,i){t.push(i.name)}),t}(),g={preview:function(e){a.preview(e)},upload:function(e,t){var i={};i[e]=t,a.upload(i)},pushFile:function(){return a.files=a.files||{},layui.each(a.chooseFiles,function(e,t){a.files[e]=t}),a.files},resetFile:function(e,t,i){var n=new File([t],i);a.files=a.files||{},a.files[e]=n}},y=function(){if("choose"!==i&&!l.auto||(l.choose&&l.choose(g),"choose"!==i))return l.before&&l.before(g),o.ie?o.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return a.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return a.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return a.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,t){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(t))||(n=!0)}),n)return a.msg("选择的图片中包含不支持的格式"),r.value=""}if(a.fileLength=function(){var t=0,i=e||a.files||a.chooseFiles||r.files;return layui.each(i,function(){t++}),t}(),l.number&&a.fileLength>l.number)return a.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(o.ie&&o.ie<10)){var F;if(layui.each(a.chooseFiles,function(e,t){if(t.size>1024*l.size){var i=l.size/1024;i=i>=1?i.toFixed(2)+"MB":l.size+"KB",r.value="",F=i}}),F)return a.msg("文件不能超过"+F)}y()}},p.prototype.reload=function(e){e=e||{},delete e.elem,delete e.bindAction;var i=this,e=i.config=t.extend({},i.config,a.config,e),n=e.elem.next();n.attr({name:e.name,accept:e.acceptMime,multiple:e.multiple})},p.prototype.events=function(){var e=this,i=e.config,a=function(t){e.chooseFiles={},layui.each(t,function(t,i){var n=(new Date).getTime();e.chooseFiles[n+"-"+t]=i})},l=function(t,n){var o=e.elemFile,a=t.length>1?t.length+"个文件":(t[0]||{}).name||o[0].value.match(/[^\/\\]+\..+/g)||[]||"";o.next().hasClass(s)&&o.next().remove(),e.upload(null,"choose"),e.isFile()||i.choose||o.after(''+a+"")};i.elem.off("upload.start").on("upload.start",function(){var o=t(this),a=o.attr("lay-data");if(a)try{a=new Function("return "+a)(),e.config=t.extend({},i,a)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+a)}e.config.item=o,e.elemFile[0].click()}),o.ie&&o.ie<10||i.elem.off("upload.over").on("upload.over",function(){var e=t(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=t(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,o){var r=t(this),u=o.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),a(u),i.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var t=this.files||[];a(t),i.auto?e.upload():l(t)}),i.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),i.elem.data("haveEvents")||(e.elemFile.on("change",function(){t(this).trigger("upload.change")}),i.elem.on("click",function(){e.isFile()||t(this).trigger("upload.start")}),i.drag&&i.elem.on("dragover",function(e){e.preventDefault(),t(this).trigger("upload.over")}).on("dragleave",function(e){t(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),t(this).trigger("upload.drop",e)}),i.bindAction.on("click",function(){t(this).trigger("upload.action")}),i.elem.data("haveEvents",!0))},a.render=function(e){var t=new p(e);return l.call(t)},e(r,a)}); -------------------------------------------------------------------------------- /static/layuimini/lib/layui-v2.5.5/lay/modules/element.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.5 MIT License By https://www.layui.com */ 2 | ;layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
  • "+(i.title||"unnaming")+"
  • ";return s[0]?s.before(r):n.append(r),o.append('
    '+(i.content||"")+"
    "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)}); --------------------------------------------------------------------------------