├── .gitignore ├── README.md ├── doc ├── btlocal.md ├── docker.md ├── images │ ├── img_1.png │ ├── img_10.png │ ├── img_11.png │ ├── img_2.png │ ├── img_3.png │ ├── img_4.png │ ├── img_5.png │ ├── img_6.png │ ├── img_7.png │ ├── img_8.png │ └── img_9.png └── local.md ├── docker ├── build │ └── docker-compose.yml ├── flyapps │ ├── docker-compose.only.yml │ └── docker-compose.yml ├── init │ ├── clean.sh │ ├── download.images │ ├── init.sh │ └── timezone ├── mariadb │ ├── docker-compose.yml │ └── server.cnf ├── redis │ └── docker-compose.yml └── scripts │ ├── build.sh │ ├── start_all.sh │ └── stop_all.sh ├── fir_admin ├── .editorconfig ├── .env.development ├── .env.production ├── .env.staging ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── LICENSE ├── README-zh.md ├── README.md ├── babel.config.js ├── build │ └── index.js ├── jest.config.js ├── jsconfig.json ├── mock │ ├── index.js │ ├── mock-server.js │ ├── table.js │ ├── user.js │ └── utils.js ├── package.json ├── postcss.config.js ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── api │ │ ├── app.js │ │ ├── developer.js │ │ ├── devices.js │ │ ├── domain.js │ │ ├── order.js │ │ ├── qiniu.js │ │ ├── report.js │ │ ├── storage.js │ │ ├── table.js │ │ ├── user.js │ │ └── wxbind.js │ ├── assets │ │ ├── 404_images │ │ │ ├── 404.png │ │ │ └── 404_cloud.png │ │ └── logo.png │ ├── components │ │ ├── Breadcrumb │ │ │ └── index.vue │ │ ├── Hamburger │ │ │ └── index.vue │ │ ├── Pagination │ │ │ └── index.vue │ │ ├── Sticky │ │ │ └── index.vue │ │ ├── SvgIcon │ │ │ └── index.vue │ │ ├── Tinymce │ │ │ ├── components │ │ │ │ └── EditorImage.vue │ │ │ ├── dynamicLoadScript.js │ │ │ ├── index.vue │ │ │ ├── plugins.js │ │ │ └── toolbar.js │ │ └── Upload │ │ │ ├── SingleImage.vue │ │ │ ├── SingleImage2.vue │ │ │ └── SingleImage3.vue │ ├── directive │ │ ├── clipboard │ │ │ ├── clipboard.js │ │ │ └── index.js │ │ ├── el-drag-dialog │ │ │ ├── drag.js │ │ │ └── index.js │ │ ├── el-table │ │ │ ├── adaptive.js │ │ │ └── index.js │ │ ├── permission │ │ │ ├── index.js │ │ │ └── permission.js │ │ ├── sticky.js │ │ └── waves │ │ │ ├── index.js │ │ │ ├── waves.css │ │ │ └── waves.js │ ├── icons │ │ ├── index.js │ │ ├── svg │ │ │ ├── dashboard.svg │ │ │ ├── example.svg │ │ │ ├── eye-open.svg │ │ │ ├── eye.svg │ │ │ ├── form.svg │ │ │ ├── link.svg │ │ │ ├── nested.svg │ │ │ ├── password.svg │ │ │ ├── table.svg │ │ │ ├── tree.svg │ │ │ └── user.svg │ │ └── svgo.yml │ ├── layout │ │ ├── components │ │ │ ├── AppMain.vue │ │ │ ├── Navbar.vue │ │ │ ├── Sidebar │ │ │ │ ├── FixiOSBug.js │ │ │ │ ├── Item.vue │ │ │ │ ├── Link.vue │ │ │ │ ├── Logo.vue │ │ │ │ ├── SidebarItem.vue │ │ │ │ └── index.vue │ │ │ └── index.js │ │ ├── index.vue │ │ └── mixin │ │ │ └── ResizeHandler.js │ ├── main.js │ ├── permission.js │ ├── router │ │ └── index.js │ ├── settings.js │ ├── store │ │ ├── getters.js │ │ ├── index.js │ │ └── modules │ │ │ ├── app.js │ │ │ ├── settings.js │ │ │ └── user.js │ ├── styles │ │ ├── element-ui.scss │ │ ├── index.scss │ │ ├── mixin.scss │ │ ├── sidebar.scss │ │ ├── transition.scss │ │ └── variables.scss │ ├── utils │ │ ├── auth.js │ │ ├── get-page-title.js │ │ ├── index.js │ │ ├── request.js │ │ ├── scroll-to.js │ │ └── validate.js │ └── views │ │ ├── 404.vue │ │ ├── appinfos │ │ ├── AppDetail.vue │ │ ├── AppReleaseDetail.vue │ │ ├── AppReleaseList.vue │ │ ├── edit.vue │ │ └── list.vue │ │ ├── authentication │ │ ├── Detail.vue │ │ └── list.vue │ │ ├── dashboard │ │ └── index.vue │ │ ├── domain │ │ ├── Detail.vue │ │ └── list.vue │ │ ├── form │ │ └── index.vue │ │ ├── login │ │ └── index.vue │ │ ├── nested │ │ ├── menu1 │ │ │ ├── index.vue │ │ │ ├── menu1-1 │ │ │ │ └── index.vue │ │ │ ├── menu1-2 │ │ │ │ ├── index.vue │ │ │ │ ├── menu1-2-1 │ │ │ │ │ └── index.vue │ │ │ │ └── menu1-2-2 │ │ │ │ │ └── index.vue │ │ │ └── menu1-3 │ │ │ │ └── index.vue │ │ └── menu2 │ │ │ └── index.vue │ │ ├── order │ │ ├── Detail.vue │ │ └── list.vue │ │ ├── report │ │ ├── Detail.vue │ │ └── list.vue │ │ ├── storage │ │ ├── Detail.vue │ │ └── list.vue │ │ ├── supersign │ │ ├── bill │ │ │ ├── add.vue │ │ │ └── list.vue │ │ ├── developer │ │ │ ├── Detail.vue │ │ │ └── list.vue │ │ ├── devices │ │ │ └── list.vue │ │ └── index.vue │ │ ├── table │ │ └── index.vue │ │ ├── tree │ │ └── index.vue │ │ ├── userinfos │ │ ├── Dropdown │ │ │ ├── Comment.vue │ │ │ ├── Platform.vue │ │ │ ├── SourceUrl.vue │ │ │ └── index.js │ │ ├── UserDetail.vue │ │ ├── edit.vue │ │ └── list.vue │ │ └── wxbind │ │ ├── Detail.vue │ │ └── list.vue ├── vue.config.js └── yarn.lock ├── fir_client ├── .gitignore ├── README.md ├── babel.config.js ├── package.json ├── public │ ├── favicon.ico │ ├── gt.js │ ├── index.html │ └── short.html ├── src │ ├── App.vue │ ├── Download.vue │ ├── assets │ │ ├── beianlogo.png │ │ ├── bg.jpeg │ │ ├── download_pattern_left.png │ │ ├── download_pattern_right.png │ │ ├── github.png │ │ ├── icon │ │ │ ├── iconfont.css │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.js │ │ │ ├── iconfont.json │ │ │ ├── iconfont.svg │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ └── iconfont.woff2 │ │ ├── imgs │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ ├── 9.png │ │ │ ├── banner1.png │ │ │ ├── banner2.jpg │ │ │ ├── banner_1.jpg │ │ │ ├── banner_2.jpg │ │ │ ├── circle.png │ │ │ ├── computer.png │ │ │ ├── contact_us_bg.jpg │ │ │ ├── qq.png │ │ │ ├── service1.jpg │ │ │ ├── service2.jpg │ │ │ ├── service3.jpg │ │ │ ├── service4.jpg │ │ │ ├── skill.png │ │ │ ├── tel.png │ │ │ ├── twitter.png │ │ │ ├── weibo.png │ │ │ └── weixin.png │ │ ├── inhouse │ │ │ ├── b1.png │ │ │ ├── b2.png │ │ │ ├── b3.png │ │ │ ├── b4.png │ │ │ ├── b5.png │ │ │ └── b6.png │ │ ├── loading.gif │ │ ├── logo.png │ │ ├── pay │ │ │ ├── order-weixin.png │ │ │ ├── pay-scan.png │ │ │ ├── pay_alipay.png │ │ │ ├── pay_selected.png │ │ │ └── pay_weixin.png │ │ ├── plane.svg │ │ ├── propeller.svg │ │ └── sign │ │ │ ├── help │ │ │ ├── sign_help_0.png │ │ │ ├── sign_help_1.png │ │ │ ├── sign_help_2.png │ │ │ ├── sign_help_3.png │ │ │ ├── sign_help_4.png │ │ │ └── sign_help_5.png │ │ │ ├── step1.jpg │ │ │ ├── step2.jpg │ │ │ ├── step3.jpg │ │ │ └── step4.jpg │ ├── components │ │ ├── FirBase.vue │ │ ├── FirDownload.vue │ │ ├── FirFooter.vue │ │ ├── FirHeader.vue │ │ ├── FirLogin.vue │ │ ├── FirRegist.vue │ │ ├── FirResetPwd.vue │ │ ├── ShortDownload.vue │ │ ├── apps │ │ │ ├── FirAppInfosBase.vue │ │ │ ├── FirAppInfosbaseinfo.vue │ │ │ ├── FirAppInfoscombo.vue │ │ │ ├── FirAppInfosdevices.vue │ │ │ ├── FirAppInfossecurity.vue │ │ │ ├── FirAppInfossupersign.vue │ │ │ ├── FirAppInfostimeline.vue │ │ │ └── FirApps.vue │ │ ├── base │ │ │ ├── AppleDeveloperBindApp.vue │ │ │ ├── BackToTop.vue │ │ │ └── BindDomain.vue │ │ ├── index │ │ │ ├── FirContact.vue │ │ │ ├── FirIndexBody.vue │ │ │ ├── FirIndexTem.vue │ │ │ ├── FirNews.vue │ │ │ └── FirService.vue │ │ └── user │ │ │ ├── FirSuperSignBase.vue │ │ │ ├── FirSuperSignHelp.vue │ │ │ ├── FirUserAdvert.vue │ │ │ ├── FirUserDomain.vue │ │ │ ├── FirUserDownload.vue │ │ │ ├── FirUserNotify.vue │ │ │ ├── FirUserOrders.vue │ │ │ ├── FirUserProfileBase.vue │ │ │ ├── FirUserProfileCertification.vue │ │ │ ├── FirUserProfileChangePwd.vue │ │ │ ├── FirUserProfileInfo.vue │ │ │ ├── FirUserQrcode.vue │ │ │ └── FirUserStorage.vue │ ├── main.js │ ├── main.short.js │ ├── restful │ │ ├── download.js │ │ └── index.js │ ├── router │ │ ├── download.js │ │ ├── index.js │ │ └── short.js │ ├── short.js │ ├── store │ │ └── index.js │ └── utils │ │ ├── base │ │ └── utils.js │ │ ├── format.js │ │ └── index.js └── vue.config.js ├── fir_ser ├── Dockerfile ├── Dockerfile.cli ├── admin │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ ├── utils │ │ ├── __init__.py │ │ ├── serializer.py │ │ └── utils.py │ └── views │ │ ├── app.py │ │ ├── celery_flower.py │ │ ├── domain.py │ │ ├── login.py │ │ ├── order.py │ │ ├── report.py │ │ ├── storage.py │ │ └── user.py ├── api │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── base_views.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── add_user_download_times.py │ │ │ ├── create_weixin_menu.py │ │ │ ├── expire_caches.py │ │ │ ├── expire_config_caches.py │ │ │ ├── restart.py │ │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── command.py │ │ │ ├── hands.py │ │ │ ├── services │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── beat.py │ │ │ │ ├── celery_base.py │ │ │ │ ├── celery_default.py │ │ │ │ ├── flower.py │ │ │ │ ├── gunicorn.py │ │ │ │ └── uwsgi.py │ │ │ └── utils.py │ │ │ ├── start.py │ │ │ ├── status.py │ │ │ ├── stop.py │ │ │ └── upgrade.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20220215_2129.py │ │ ├── 0003_appbundleidblacklist.py │ │ ├── 0004_auto_20220330_0009.py │ │ ├── 0005_auto_20220412_1744.py │ │ ├── 0006_auto_20220513_0843.py │ │ ├── 0007_auto_20220725_1612.py │ │ └── __init__.py │ ├── models.py │ ├── tasks.py │ ├── urls.py │ ├── utils │ │ ├── __init__.py │ │ ├── apputils.py │ │ ├── auth │ │ │ ├── captcha.py │ │ │ ├── geetest.py │ │ │ ├── mfa.py │ │ │ ├── util.py │ │ │ └── weixin.py │ │ ├── ctasks.py │ │ ├── modelutils.py │ │ ├── response.py │ │ ├── serializer.py │ │ ├── signalutils.py │ │ └── utils.py │ └── views │ │ ├── __init__.py │ │ ├── advert.py │ │ ├── apps.py │ │ ├── domain.py │ │ ├── download.py │ │ ├── getip.py │ │ ├── login.py │ │ ├── login_wx.py │ │ ├── logout.py │ │ ├── notify.py │ │ ├── order.py │ │ ├── personalconfig.py │ │ ├── report.py │ │ ├── storage.py │ │ ├── thirdlogin.py │ │ └── uploads.py ├── cli.py ├── cli │ ├── __init__.py │ ├── apps.py │ ├── urls.py │ └── views │ │ ├── apps.py │ │ ├── login.py │ │ └── uploads.py ├── common │ ├── base │ │ ├── baseutils.py │ │ ├── daobase.py │ │ └── magic.py │ ├── cache │ │ ├── invalid.py │ │ ├── state.py │ │ └── storage.py │ ├── constants.py │ ├── core │ │ ├── auth.py │ │ ├── dbrouter.py │ │ ├── exception.py │ │ ├── response.py │ │ ├── signals.py │ │ ├── sysconfig.py │ │ └── throttle.py │ ├── libs │ │ ├── apple │ │ │ └── appleapiv3.py │ │ ├── geetest │ │ │ ├── geetest_lib.py │ │ │ ├── geetest_lib_result.py │ │ │ └── geetest_utils.py │ │ ├── mp │ │ │ ├── __init__.py │ │ │ ├── chat │ │ │ │ ├── __init__.py │ │ │ │ ├── receive.py │ │ │ │ └── reply.py │ │ │ ├── ierror.py │ │ │ ├── utils.py │ │ │ └── wechat.py │ │ ├── pay │ │ │ ├── ali.py │ │ │ ├── alipay │ │ │ │ ├── __init__.py │ │ │ │ ├── exceptions.py │ │ │ │ └── loggers.py │ │ │ ├── util.py │ │ │ ├── wx.py │ │ │ └── wxpay │ │ │ │ ├── __init__.py │ │ │ │ └── core.py │ │ ├── sendmsg │ │ │ ├── aliyunApi.py │ │ │ ├── emailApi.py │ │ │ ├── jiguangApi.py │ │ │ ├── template_content.py │ │ │ └── templates │ │ │ │ ├── download_times_not_enough.html │ │ │ │ ├── download_times_over_limit.html │ │ │ │ ├── pay_success.html │ │ │ │ ├── userinfo │ │ │ │ ├── change_userinfo.html │ │ │ │ ├── code_notify.html │ │ │ │ ├── login_code.html │ │ │ │ ├── register_code.html │ │ │ │ └── reset_password.html │ │ │ │ └── xsign │ │ │ │ ├── app_sign_failed.html │ │ │ │ ├── app_sign_over_limit.html │ │ │ │ ├── apple_developer_cert_expired.html │ │ │ │ ├── apple_developer_devices_over_limit.html │ │ │ │ ├── apple_developer_unavailable.html │ │ │ │ └── timing_task_notify.html │ │ └── storage │ │ │ ├── aliyunApi.py │ │ │ ├── localApi.py │ │ │ └── qiniuApi.py │ ├── notify │ │ ├── notify.py │ │ ├── ntasks.py │ │ └── utils.py │ └── utils │ │ ├── caches.py │ │ ├── download.py │ │ ├── pending.py │ │ ├── sendmsg.py │ │ ├── storage.py │ │ └── token.py ├── config.py ├── entrypoint.sh ├── files │ ├── embedded.mobileprovision │ └── head_img.jpeg ├── fir_ser │ ├── __init__.py │ ├── asgi.py │ ├── celery.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── logs │ └── .gitkeep ├── manage.py ├── nginx-vhost.conf ├── nginx.conf ├── readme.txt ├── requirements.txt ├── requirements │ └── rpm_requirements.txt ├── supersign │ └── scripts │ │ └── apple_api.rb ├── tests │ ├── django_test.py │ └── postudid.py ├── tmp │ └── .gitkeep ├── uwsgi.conf ├── xsign │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20220405_1452.py │ │ ├── 0003_deviceabnormaludid_deviceblackudid.py │ │ ├── 0004_udidsyncdeveloper_device_class.py │ │ └── __init__.py │ ├── models.py │ ├── tasks.py │ ├── urls.py │ ├── utils │ │ ├── __init__.py │ │ ├── ctasks.py │ │ ├── iossignapi.py │ │ ├── iproxy.py │ │ ├── modelutils.py │ │ ├── serializer.py │ │ ├── signals.py │ │ ├── supersignutils.py │ │ └── utils.py │ └── views │ │ ├── __init__.py │ │ ├── admin │ │ ├── __init__.py │ │ └── supersign.py │ │ ├── appinfo.py │ │ ├── download.py │ │ ├── receiveudids.py │ │ └── supersign.py └── zsign-1.1.2.tar.gz ├── mailhtml ├── build.js ├── package-lock.json ├── package.json └── src │ ├── app_sign_failed.mjml.html │ ├── app_sign_over_limit.mjml.html │ ├── apple_developer_cert_expired.mjml.html │ ├── apple_developer_devices_over_limit.mjml.html │ ├── apple_developer_unavailable.mjml.html │ ├── base │ ├── footer.mjml │ └── username.mjml │ ├── change_userinfo.mjml.html │ ├── code_notify.mjml.html │ ├── download_times_not_enough.mjml.html │ ├── download_times_over_limit.mjml.html │ ├── index.html │ ├── index.mjml.html │ ├── login_code.mjml.html │ ├── pay_success.mjml.html │ ├── register_code.mjml.html │ ├── reset_password.mjml.html │ └── timing_task_notify.mjml.html └── nginx.conf.d ├── app.hehelucky.cn.key ├── app.hehelucky.cn.pem └── flyapps-vhost.conf /.gitignore: -------------------------------------------------------------------------------- 1 | fir_ser/.idea/ 2 | fir_ser/db.sqlite3 3 | *.pyc 4 | fir_ser/venv/ 5 | fir_client/.idea 6 | fir_admin/.idea/.gitignore 7 | fir_admin/.idea/ 8 | fir_client/yarn.lock 9 | fir_download/package-lock.json 10 | fir_client/src/assets/down_left.png 11 | fir_client/src/assets/down_right.png 12 | mailhtml/build/ 13 | mailhtml/node_modules/ 14 | mailhtml/.idea/ 15 | fir_client/dist_index/ 16 | fir_client/dist_short/ 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 用与应用分发,苹果超级签名 2 | #### 部署前准备 3 | - 备案域名【至少需要一个域名,以下可通过子域名部署】 4 | - API域名 5 | - 前端web域名 6 | - 下载页域名 7 | - 下载页域名可配置多个 8 | - 存储域名(使用阿里云oss存储) 9 | - ssl证书 10 | - API域名证书 11 | - 存储域名证书(使用阿里云oss存储) 12 | - 前端web域名证书(可选) 13 | - Centos8Stream 服务器 14 | - 如果使用oss存储,则带宽为1M,若使用本地存储,则带宽越大越好 15 | - 如果使用超级签,最低配置为2cpu 4G内存,若干不使用签名,则1cpu2G就行 16 | - 阿里云短信或极光短信服务【可选一个,主要用与注册,重置密码】 17 | - 阿里云短信 18 | - 极光短信 19 | - 邮箱服务【可选,用与注册,重置密码,通知信息】 20 | - 阿里云OSS存储【可选】 21 | - [sts授权配置](https://help.aliyun.com/document_detail/100624.html) 22 | - 阿里云CDN【可选,用与加速访问】 23 | - 极验验证【可选,滑动验证服务】 24 | - 微信公众号【可选,用与微信扫描登录】 25 | - 阿里云支付【可选,用与购买下载次数】 26 | - 微信支付【可选,用与购买下载次数】 27 | 28 | #### 自用搭建建议 29 | - 阿里云服务器需要1cpu 2G内存,无需系统盘,如果使用超级签,可以适当增加配置 30 | - 需要阿里云OSS存储和阿里云CDN,并且OSS存储和阿里云服务器部署同一个地区 31 | - 可以申请一个极验进行滑动验证,或者开启验证码验证 32 | - 阿里云备案域名:api和前端可以使用一个域名,下载页单独域名 33 | 34 | #### 部署必备资料 35 | - 域名证书 36 | - web域名和证书 37 | - api域名和证书 38 | - 下载页域名(可配置证书) 39 | - 存储域名和证书 40 | - 本地存储,则该域名和证书可以和api域名证书一致 41 | - 阿里云oss存储 42 | - 开启cdn,需要新域名和证书 43 | - 不开启,无需域名和证书 44 | - Centos8Stream 服务器 45 | 46 | ## 部署方式 47 | 48 | ### 1.[Docker 部署](./doc/docker.md) 【推荐, 内存比本地部署高一些】 49 | 50 | ### 2.[本地部署](./doc/local.md) 51 | 52 | ### 3.[宝塔面板部署](./doc/btlocal.md) 53 | 54 | ### 功能预览 55 | ![img_1.png](./doc/images/img_1.png) 56 | ![img_2.png](./doc/images/img_2.png) 57 | ![img_3.png](./doc/images/img_3.png) 58 | ![img_4.png](./doc/images/img_4.png) 59 | ![img_5.png](./doc/images/img_5.png) 60 | ![img_6.png](./doc/images/img_6.png) 61 | ![img_7.png](./doc/images/img_7.png) 62 | ![img_8.png](./doc/images/img_8.png) 63 | ![img_9.png](./doc/images/img_9.png) 64 | ![img_10.png](./doc/images/img_10.png) 65 | ![img_11.png](./doc/images/img_11.png) -------------------------------------------------------------------------------- /doc/images/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_1.png -------------------------------------------------------------------------------- /doc/images/img_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_10.png -------------------------------------------------------------------------------- /doc/images/img_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_11.png -------------------------------------------------------------------------------- /doc/images/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_2.png -------------------------------------------------------------------------------- /doc/images/img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_3.png -------------------------------------------------------------------------------- /doc/images/img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_4.png -------------------------------------------------------------------------------- /doc/images/img_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_5.png -------------------------------------------------------------------------------- /doc/images/img_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_6.png -------------------------------------------------------------------------------- /doc/images/img_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_7.png -------------------------------------------------------------------------------- /doc/images/img_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_8.png -------------------------------------------------------------------------------- /doc/images/img_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/doc/images/img_9.png -------------------------------------------------------------------------------- /docker/flyapps/docker-compose.only.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | networks: 4 | flyapps: 5 | external: true 6 | name: flyapps 7 | 8 | services: 9 | flyapps: 10 | container_name: flyapps 11 | restart: always 12 | image: 'nineven/flyapps' 13 | working_dir: /data/fir_ser 14 | volumes: 15 | - /etc/localtime:/etc/localtime:ro 16 | - ../../fir_ser:/data/fir_ser 17 | - ../../data/flyapps/files:/data/fir_ser/files 18 | - ../../data/logs/flyapps/:/data/fir_ser/logs 19 | - ../../data/flyapps/supersign/:/data/fir_ser/supersign 20 | - ../../nginx.conf.d:/data/cert 21 | networks: 22 | flyapps: 23 | ipv4_address: 172.31.31.100 24 | external_links: 25 | - mariadb:mariadb 26 | - redis:redis 27 | sysctls: 28 | - net.core.somaxconn=4096 29 | extra_hosts: 30 | - "mariadb:172.31.31.1" 31 | - "redis:172.31.31.1" 32 | -------------------------------------------------------------------------------- /docker/flyapps/docker-compose.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | networks: 4 | flyapps: 5 | external: true 6 | name: flyapps 7 | 8 | services: 9 | flyapps: 10 | container_name: flyapps 11 | restart: always 12 | image: 'nineven/flyapps' 13 | working_dir: /data/fir_ser 14 | volumes: 15 | - /etc/localtime:/etc/localtime:ro 16 | - ../../fir_ser:/data/fir_ser 17 | - ../../data/flyapps/files:/data/fir_ser/files 18 | - ../../data/logs/flyapps/:/data/fir_ser/logs 19 | - ../../data/flyapps/supersign/:/data/fir_ser/supersign 20 | - ../../nginx.conf.d:/data/cert 21 | networks: 22 | flyapps: 23 | ipv4_address: 172.31.31.100 24 | external_links: 25 | - mariadb:mariadb 26 | - redis:redis 27 | sysctls: 28 | - net.core.somaxconn=4096 29 | nginx: 30 | container_name: nginx 31 | restart: always 32 | image: 'nginx:1.21.3' 33 | volumes: 34 | - /etc/localtime:/etc/localtime:ro 35 | - ../../data/web:/data 36 | - ../../nginx.conf.d:/etc/nginx/conf.d 37 | - ../../data/logs/nginx:/var/log/nginx 38 | networks: 39 | flyapps: 40 | ipv4_address: 172.31.31.200 41 | ports: 42 | - 80:80 43 | - 443:443 44 | - 3448:3448 45 | external_links: 46 | - flyapps:flyapps 47 | depends_on: 48 | - flyapps 49 | -------------------------------------------------------------------------------- /docker/init/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # 4 | 5 | for i in nginx flyapps mariadb redis buildclient buildshort buildadmin ;do echo $i;docker rm -f $i;done 6 | 7 | docker network rm flyapps 8 | -------------------------------------------------------------------------------- /docker/init/download.images: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # 4 | 5 | 6 | docker pull 'bitnami/mariadb:10.7.3' 7 | docker pull 'bitnami/redis:6.2.7' 8 | docker pull 'nginx:1.21.3' 9 | docker pull 'nineven/flyapps:latest' 10 | docker pull 'node:14.17.3' 11 | -------------------------------------------------------------------------------- /docker/init/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # 4 | 5 | which dockerd 6 | if [ $? -ne 0 ];then 7 | dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo 8 | dnf install docker-ce -y 9 | fi 10 | #which docker-compose 11 | #if [ $? -ne 0 ];then 12 | # curl -L https://get.daocloud.io/docker/compose/releases/download/v2.5.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose 13 | # chmod +x /usr/local/bin/docker-compose 14 | #fi 15 | 16 | data_path="$(dirname $(dirname `pwd`))/data" 17 | mkdir -pv ${data_path}/{flyapps/files,web,mariadb,redis,logs/{mariadb,nginx,flyapps}} 18 | \cp $(dirname $(dirname `pwd`))/fir_ser/files/head_img.jpeg ${data_path}/flyapps/files/ 19 | chown 1001.1001 -R ${data_path}/{flyapps,web,mariadb,redis,logs/{mariadb,nginx,flyapps}} 20 | chown 101.101 -R ${data_path}/{flyapps,logs/flyapps} 21 | systemctl start docker && docker network create flyapps --driver bridge --subnet=172.31.31.0/24 --gateway=172.31.31.1 22 | systemctl enable docker 23 | systemctl status docker 24 | 25 | -------------------------------------------------------------------------------- /docker/init/timezone: -------------------------------------------------------------------------------- 1 | Asia/Shanghai 2 | -------------------------------------------------------------------------------- /docker/mariadb/docker-compose.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | networks: 6 | flyapps: 7 | external: true 8 | name: flyapps 9 | 10 | services: 11 | 12 | mariadb: 13 | image: bitnami/mariadb:10.7.3 14 | container_name: mariadb 15 | restart: always 16 | environment: 17 | - MARIADB_ROOT_PASSWORD=rootIPD.xx2.19 18 | - MARIADB_DATABASE=flyapps 19 | - MARIADB_USER=flyuser 20 | - MARIADB_PASSWORD=KGzKjZpWBp4R4RSa 21 | #- ALLOW_EMPTY_PASSWORD=yes 22 | - MARIADB_ENABLE_SLOW_QUERY=1 23 | - MARIADB_LONG_QUERY_TIME=3 24 | - MARIADB_SKIP_TEST_DB=yes 25 | - MARIADB_EXTRA_FLAGS=--max-connect-errors=3000 --max_connections=30000 26 | ports: 27 | - 3306:3306 28 | networks: 29 | flyapps: 30 | ipv4_address: 172.31.31.90 31 | volumes: 32 | - ../init/timezone:/etc/timezone:ro 33 | - /etc/localtime:/etc/localtime:ro 34 | - ../../data/mariadb:/bitnami/mariadb/data 35 | - ../../data/logs/mariadb:/data/logs/mariadb 36 | - ./server.cnf:/opt/bitnami/mariadb/conf/my_custom.cnf:ro 37 | 38 | # adminer: 39 | # image: adminer 40 | # ports: 41 | # - 8080:8080 42 | # networks: 43 | # - fungame 44 | -------------------------------------------------------------------------------- /docker/redis/docker-compose.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | networks: 5 | flyapps: 6 | external: true 7 | name: flyapps 8 | 9 | services: 10 | redis: 11 | image: 'bitnami/redis:6.2.7' 12 | container_name: redis 13 | restart: always 14 | volumes: 15 | - ../init/timezone:/etc/timezone:ro 16 | - /etc/localtime:/etc/localtime:ro 17 | - ../../data/redis:/bitnami/redis/data 18 | environment: 19 | #- REDIS_REPLICATION_MODE=master 20 | #- ALLOW_EMPTY_PASSWORD=yes 21 | - REDIS_PASSWORD=nineven 22 | networks: 23 | - flyapps 24 | #ports: 25 | # - 6379:6379 26 | -------------------------------------------------------------------------------- /docker/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # 4 | 5 | cd ../build/ 6 | docker compose up buildclient buildadmin 7 | -------------------------------------------------------------------------------- /docker/scripts/start_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # 4 | 5 | cd ../mariadb/ && docker compose up -d 6 | cd ../redis/ && docker compose up -d 7 | 8 | cd ../flyapps/ && docker compose up -d 9 | docker logs -f flyapps 10 | -------------------------------------------------------------------------------- /docker/scripts/stop_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # 4 | 5 | 6 | cd ../flyapps/ && docker compose down 7 | 8 | cd ../redis/ && docker compose down 9 | 10 | cd ../mariadb/ && docker compose down 11 | -------------------------------------------------------------------------------- /fir_admin/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /fir_admin/.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | VUE_APP_BASE_API = 'https://flyapps.cn/api/v3/fir/server' 6 | #VUE_APP_BASE_API = 'https://app.hehelucky.cn/api/v3/fir/server' 7 | -------------------------------------------------------------------------------- /fir_admin/.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/api/v3/fir/server' 6 | 7 | -------------------------------------------------------------------------------- /fir_admin/.env.staging: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | # just a flag 4 | ENV = 'staging' 5 | 6 | # base api 7 | VUE_APP_BASE_API = '/stage-api' 8 | 9 | -------------------------------------------------------------------------------- /fir_admin/.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /fir_admin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | package-lock.json 8 | tests/**/coverage/ 9 | 10 | # Editor directories and files 11 | .idea 12 | .vscode 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | -------------------------------------------------------------------------------- /fir_admin/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10 3 | script: npm run test 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /fir_admin/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present PanJiaChen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /fir_admin/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app 4 | '@vue/cli-plugin-babel/preset' 5 | ], 6 | 'env': { 7 | 'development': { 8 | // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). 9 | // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. 10 | // https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html 11 | 'plugins': ['dynamic-import-node'] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /fir_admin/build/index.js: -------------------------------------------------------------------------------- 1 | const { run } = require('runjs') 2 | const chalk = require('chalk') 3 | const config = require('../vue.config.js') 4 | const rawArgv = process.argv.slice(2) 5 | const args = rawArgv.join(' ') 6 | 7 | if (process.env.npm_config_preview || rawArgv.includes('--preview')) { 8 | const report = rawArgv.includes('--report') 9 | 10 | run(`vue-cli-service build ${args}`) 11 | 12 | const port = 9526 13 | const publicPath = config.publicPath 14 | 15 | var connect = require('connect') 16 | var serveStatic = require('serve-static') 17 | const app = connect() 18 | 19 | app.use( 20 | publicPath, 21 | serveStatic('./dist', { 22 | index: ['index.html', '/'] 23 | }) 24 | ) 25 | 26 | app.listen(port, function () { 27 | console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) 28 | if (report) { 29 | console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) 30 | } 31 | 32 | }) 33 | } else { 34 | run(`vue-cli-service build ${args}`) 35 | } 36 | -------------------------------------------------------------------------------- /fir_admin/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 6 | 'jest-transform-stub', 7 | '^.+\\.jsx?$': 'babel-jest' 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1' 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 15 | ], 16 | collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'], 17 | coverageDirectory: '/tests/unit/coverage', 18 | // 'collectCoverage': true, 19 | 'coverageReporters': [ 20 | 'lcov', 21 | 'text-summary' 22 | ], 23 | testURL: 'http://localhost/' 24 | } 25 | -------------------------------------------------------------------------------- /fir_admin/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": ["src/*"] 6 | } 7 | }, 8 | "exclude": ["node_modules", "dist"] 9 | } 10 | -------------------------------------------------------------------------------- /fir_admin/mock/index.js: -------------------------------------------------------------------------------- 1 | const Mock = require('mockjs') 2 | const { param2Obj } = require('./utils') 3 | 4 | const user = require('./user') 5 | const table = require('./table') 6 | 7 | const mocks = [ 8 | ...user, 9 | ...table 10 | ] 11 | 12 | // for front mock 13 | // please use it cautiously, it will redefine XMLHttpRequest, 14 | // which will cause many of your third-party libraries to be invalidated(like progress event). 15 | function mockXHR() { 16 | // mock patch 17 | // https://github.com/nuysoft/Mock/issues/300 18 | Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send 19 | Mock.XHR.prototype.send = function() { 20 | if (this.custom.xhr) { 21 | this.custom.xhr.withCredentials = this.withCredentials || false 22 | 23 | if (this.responseType) { 24 | this.custom.xhr.responseType = this.responseType 25 | } 26 | } 27 | this.proxy_send(...arguments) 28 | } 29 | 30 | function XHR2ExpressReqWrap(respond) { 31 | return function(options) { 32 | let result = null 33 | if (respond instanceof Function) { 34 | const { body, type, url } = options 35 | // https://expressjs.com/en/4x/api.html#req 36 | result = respond({ 37 | method: type, 38 | body: JSON.parse(body), 39 | query: param2Obj(url) 40 | }) 41 | } else { 42 | result = respond 43 | } 44 | return Mock.mock(result) 45 | } 46 | } 47 | 48 | for (const i of mocks) { 49 | Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) 50 | } 51 | } 52 | 53 | module.exports = { 54 | mocks, 55 | mockXHR 56 | } 57 | 58 | -------------------------------------------------------------------------------- /fir_admin/mock/table.js: -------------------------------------------------------------------------------- 1 | const Mock = require('mockjs') 2 | 3 | const data = Mock.mock({ 4 | 'items|30': [{ 5 | id: '@id', 6 | title: '@sentence(10, 20)', 7 | 'status|1': ['published', 'draft', 'deleted'], 8 | author: 'name', 9 | display_time: '@datetime', 10 | pageviews: '@integer(300, 5000)' 11 | }] 12 | }) 13 | 14 | module.exports = [ 15 | { 16 | url: '/vue-admin-template/table/list', 17 | type: 'get', 18 | response: config => { 19 | const items = data.items 20 | return { 21 | code: 20000, 22 | data: { 23 | total: items.length, 24 | items: items 25 | } 26 | } 27 | } 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /fir_admin/mock/user.js: -------------------------------------------------------------------------------- 1 | 2 | const tokens = { 3 | admin: { 4 | token: 'admin-token' 5 | }, 6 | editor: { 7 | token: 'editor-token' 8 | } 9 | } 10 | 11 | const users = { 12 | 'admin-token': { 13 | roles: ['admin'], 14 | introduction: 'I am a super administrator', 15 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 16 | name: 'Super Admin' 17 | }, 18 | 'editor-token': { 19 | roles: ['editor'], 20 | introduction: 'I am an editor', 21 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 22 | name: 'Normal Editor' 23 | } 24 | } 25 | 26 | module.exports = [ 27 | // user login 28 | { 29 | url: '/vue-admin-template/user/login', 30 | type: 'post', 31 | response: config => { 32 | const { username } = config.body 33 | const token = tokens[username] 34 | 35 | // mock error 36 | if (!token) { 37 | return { 38 | code: 60204, 39 | message: 'Account and password are incorrect.' 40 | } 41 | } 42 | 43 | return { 44 | code: 20000, 45 | data: token 46 | } 47 | } 48 | }, 49 | 50 | // get user info 51 | { 52 | url: '/vue-admin-template/user/info\.*', 53 | type: 'get', 54 | response: config => { 55 | const { token } = config.query 56 | const info = users[token] 57 | 58 | // mock error 59 | if (!info) { 60 | return { 61 | code: 50008, 62 | message: 'Login failed, unable to get user details.' 63 | } 64 | } 65 | 66 | return { 67 | code: 20000, 68 | data: info 69 | } 70 | } 71 | }, 72 | 73 | // user logout 74 | { 75 | url: '/vue-admin-template/user/logout', 76 | type: 'post', 77 | response: _ => { 78 | return { 79 | code: 20000, 80 | data: 'success' 81 | } 82 | } 83 | } 84 | ] 85 | -------------------------------------------------------------------------------- /fir_admin/mock/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} url 3 | * @returns {Object} 4 | */ 5 | function param2Obj(url) { 6 | const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') 7 | if (!search) { 8 | return {} 9 | } 10 | const obj = {} 11 | const searchArr = search.split('&') 12 | searchArr.forEach(v => { 13 | const index = v.indexOf('=') 14 | if (index !== -1) { 15 | const name = v.substring(0, index) 16 | const val = v.substring(index + 1, v.length) 17 | obj[name] = val 18 | } 19 | }) 20 | return obj 21 | } 22 | 23 | module.exports = { 24 | param2Obj 25 | } 26 | -------------------------------------------------------------------------------- /fir_admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fir_admin", 3 | "version": "1.1.0", 4 | "description": "fir admin", 5 | "author": "nineven@qq.com", 6 | "scripts": { 7 | "dev": "vue-cli-service serve", 8 | "build:prod": "vue-cli-service build", 9 | "build:stage": "vue-cli-service build --mode staging", 10 | "preview": "node build/index.js --preview", 11 | "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", 12 | "lint": "eslint --ext .js,.vue src", 13 | "test:unit": "jest --clearCache && vue-cli-service test:unit", 14 | "test:ci": "npm run lint && npm run test:unit" 15 | }, 16 | "dependencies": { 17 | "axios": "0.18.1", 18 | "core-js": "3.6.5", 19 | "element-ui": "2.13.2", 20 | "js-base64": "2.5.2", 21 | "js-cookie": "2.2.0", 22 | "normalize.css": "7.0.0", 23 | "nprogress": "0.2.0", 24 | "path-to-regexp": "2.4.0", 25 | "vue": "2.6.10", 26 | "vue-router": "3.0.6", 27 | "vuex": "3.1.0" 28 | }, 29 | "devDependencies": { 30 | "@vue/cli-plugin-babel": "4.4.4", 31 | "@vue/cli-plugin-eslint": "4.4.4", 32 | "@vue/cli-plugin-unit-jest": "4.4.4", 33 | "@vue/cli-service": "4.4.4", 34 | "@vue/test-utils": "1.0.0-beta.29", 35 | "autoprefixer": "9.5.1", 36 | "babel-eslint": "10.1.0", 37 | "babel-jest": "23.6.0", 38 | "babel-plugin-dynamic-import-node": "2.3.3", 39 | "chalk": "2.4.2", 40 | "connect": "3.6.6", 41 | "eslint": "6.7.2", 42 | "eslint-plugin-vue": "6.2.2", 43 | "html-webpack-plugin": "3.2.0", 44 | "mockjs": "1.0.1-beta3", 45 | "runjs": "4.3.2", 46 | "sass": "1.26.8", 47 | "sass-loader": "8.0.2", 48 | "script-ext-html-webpack-plugin": "2.1.3", 49 | "serve-static": "1.13.2", 50 | "svg-sprite-loader": "4.1.3", 51 | "svgo": "1.2.2", 52 | "vue-template-compiler": "2.6.10" 53 | }, 54 | "browserslist": [ 55 | "> 1%", 56 | "last 2 versions" 57 | ], 58 | "engines": { 59 | "node": ">=8.9", 60 | "npm": ">= 3.0.0" 61 | }, 62 | "license": "MIT" 63 | } 64 | -------------------------------------------------------------------------------- /fir_admin/postcss.config.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | 'plugins': { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | 'autoprefixer': {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /fir_admin/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_admin/public/favicon.ico -------------------------------------------------------------------------------- /fir_admin/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= webpackConfig.name %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /fir_admin/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /fir_admin/src/api/app.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getAppList(query) { 4 | return request({ 5 | url: '/app/info', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function getAppInfos(pk) { 12 | return request({ 13 | url: '/app/info/' + pk, 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function updateAppInfo(pk, data) { 19 | return request({ 20 | url: '/app/info/' + pk, 21 | method: 'put', 22 | data 23 | }) 24 | } 25 | 26 | export function deleteApp(pk) { 27 | return request({ 28 | url: '/app/info/' + pk, 29 | method: 'delete' 30 | }) 31 | } 32 | 33 | export function getAppReleaseList(query) { 34 | return request({ 35 | url: '/app/release/info', 36 | method: 'get', 37 | params: query 38 | }) 39 | } 40 | 41 | export function getAppReleaseInfos(pk) { 42 | return request({ 43 | url: '/app/release/info/' + pk, 44 | method: 'get' 45 | }) 46 | } 47 | 48 | export function updateReleaseAppInfo(pk, data) { 49 | return request({ 50 | url: '/app/release/info/' + pk, 51 | method: 'put', 52 | data 53 | }) 54 | } 55 | export function downloadAppReleaseInfos(data) { 56 | return request({ 57 | url: '/app/release/info', 58 | method: 'post', 59 | data 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /fir_admin/src/api/developer.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getDeveloperList(query) { 4 | return request({ 5 | url: '/developer/info', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function getDeveloperInfo(pk) { 12 | return request({ 13 | url: '/developer/info/' + pk, 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function updatedeveloperInfo(pk, data) { 19 | return request({ 20 | url: '/developer/info/' + pk, 21 | method: 'put', 22 | data 23 | }) 24 | } 25 | 26 | export function getBillList(query) { 27 | return request({ 28 | url: '/bill/info', 29 | method: 'get', 30 | params: query 31 | }) 32 | } 33 | 34 | export function addBillInfo(data) { 35 | return request({ 36 | url: '/bill/info', 37 | method: 'post', 38 | data 39 | }) 40 | } 41 | export function delBillInfo(pk) { 42 | return request({ 43 | url: '/bill/info/' + pk, 44 | method: 'delete' 45 | }) 46 | } 47 | 48 | export function getUserBillInfo(query) { 49 | return request({ 50 | url: '/bill/userinfo', 51 | method: 'get', 52 | params: query 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /fir_admin/src/api/devices.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getDevicesList(query) { 4 | return request({ 5 | url: '/devices/info', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /fir_admin/src/api/domain.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getDomainList(query) { 4 | return request({ 5 | url: '/domain/info', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function getDomainInfos(pk) { 12 | return request({ 13 | url: '/domain/info/' + pk, 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function updateDomainInfo(pk, data) { 19 | return request({ 20 | url: '/domain/info/' + pk, 21 | method: 'put', 22 | data 23 | }) 24 | } 25 | 26 | export function deleteDomain(pk) { 27 | return request({ 28 | url: '/domain/info/' + pk, 29 | method: 'delete' 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /fir_admin/src/api/order.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getOrderList(query) { 4 | return request({ 5 | url: '/order/info', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function getOrderInfo(pk) { 12 | return request({ 13 | url: '/order/info/' + pk, 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function updateOrderInfo(pk, data) { 19 | return request({ 20 | url: '/order/info/' + pk, 21 | method: 'put', 22 | data 23 | }) 24 | } 25 | export function deleteOrderInfo(pk) { 26 | return request({ 27 | url: '/order/info/' + pk, 28 | method: 'delete' 29 | }) 30 | } 31 | export function createOrderInfo(data) { 32 | return request({ 33 | url: '/order/info', 34 | method: 'post', 35 | data 36 | }) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /fir_admin/src/api/qiniu.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getToken() { 4 | return request({ 5 | url: '/qiniu/upload/token', // 假地址 自行替换 6 | method: 'get' 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /fir_admin/src/api/report.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getAppReportList(query) { 4 | return request({ 5 | url: '/report/info', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function getAppReportIfo(pk) { 12 | return request({ 13 | url: '/report/info/' + pk, 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function updateAppReportIfo(pk, data) { 19 | return request({ 20 | url: '/report/info/' + pk, 21 | method: 'put', 22 | data 23 | }) 24 | } 25 | 26 | export function deleteAppReportIfo(pk) { 27 | return request({ 28 | url: '/report/info/' + pk, 29 | method: 'delete' 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /fir_admin/src/api/storage.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getStorageList(query) { 4 | return request({ 5 | url: '/storage/info', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function getStorageInfo(pk) { 12 | return request({ 13 | url: '/storage/info/' + pk, 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function updateStorageInfo(pk, data) { 19 | return request({ 20 | url: '/storage/info/' + pk, 21 | method: 'put', 22 | data 23 | }) 24 | } 25 | 26 | export function changeStorageInfo(data) { 27 | return request({ 28 | url: '/storage/change', 29 | method: 'put', 30 | data 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /fir_admin/src/api/table.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getList(params) { 4 | return request({ 5 | url: '/vue-admin-template/table/list', 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /fir_admin/src/api/user.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function login(data) { 4 | return request({ 5 | url: '/login', 6 | method: 'post', 7 | data 8 | }) 9 | } 10 | 11 | export function getInfo(token) { 12 | return request({ 13 | url: '/user/info', 14 | method: 'get', 15 | params: { token } 16 | }) 17 | } 18 | 19 | export function logout() { 20 | return request({ 21 | url: '/user/logout', 22 | method: 'post' 23 | }) 24 | } 25 | 26 | export function getUserInfos(query) { 27 | return request({ 28 | url: '/userinfo', 29 | method: 'get', 30 | params: query 31 | }) 32 | } 33 | 34 | export function updateUserInfo(data) { 35 | return request({ 36 | url: '/userinfo', 37 | method: 'put', 38 | data 39 | }) 40 | } 41 | 42 | export function getCertificationInfo(query) { 43 | return request({ 44 | url: '/certification/info', 45 | method: 'get', 46 | params: query 47 | }) 48 | } 49 | export function updateCertificationInfo(data) { 50 | return request({ 51 | url: '/certification/info', 52 | method: 'put', 53 | data 54 | }) 55 | } 56 | 57 | export function resetPassword(data) { 58 | return request({ 59 | url: '/user/info', 60 | method: 'post', 61 | data 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /fir_admin/src/api/wxbind.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getWxBindList(query) { 4 | return request({ 5 | url: '/wxbind/info', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function getWxBindInfos(pk) { 12 | return request({ 13 | url: '/wxbind/info/' + pk, 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function updateWxBindInfo(pk,data) { 19 | return request({ 20 | url: '/wxbind/info/' + pk, 21 | method: 'put', 22 | data 23 | }) 24 | } 25 | 26 | export function deleteWxBind(pk) { 27 | return request({ 28 | url: '/wxbind/info/' + pk, 29 | method: 'delete' 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /fir_admin/src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_admin/src/assets/404_images/404.png -------------------------------------------------------------------------------- /fir_admin/src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_admin/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /fir_admin/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_admin/src/assets/logo.png -------------------------------------------------------------------------------- /fir_admin/src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 45 | -------------------------------------------------------------------------------- /fir_admin/src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 47 | 48 | 63 | -------------------------------------------------------------------------------- /fir_admin/src/components/Tinymce/dynamicLoadScript.js: -------------------------------------------------------------------------------- 1 | let callbacks = [] 2 | 3 | function loadedTinymce() { 4 | // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144 5 | // check is successfully downloaded script 6 | return window.tinymce 7 | } 8 | 9 | const dynamicLoadScript = (src, callback) => { 10 | const existingScript = document.getElementById(src) 11 | const cb = callback || function() {} 12 | 13 | if (!existingScript) { 14 | const script = document.createElement('script') 15 | script.src = src // src url for the third-party library being loaded. 16 | script.id = src 17 | document.body.appendChild(script) 18 | callbacks.push(cb) 19 | const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd 20 | onEnd(script) 21 | } 22 | 23 | if (existingScript && cb) { 24 | if (loadedTinymce()) { 25 | cb(null, existingScript) 26 | } else { 27 | callbacks.push(cb) 28 | } 29 | } 30 | 31 | function stdOnEnd(script) { 32 | script.onload = function() { 33 | // this.onload = null here is necessary 34 | // because even IE9 works not like others 35 | this.onerror = this.onload = null 36 | for (const cb of callbacks) { 37 | cb(null, script) 38 | } 39 | callbacks = null 40 | } 41 | script.onerror = function() { 42 | this.onerror = this.onload = null 43 | cb(new Error('Failed to load ' + src), script) 44 | } 45 | } 46 | 47 | function ieOnEnd(script) { 48 | script.onreadystatechange = function() { 49 | if (this.readyState !== 'complete' && this.readyState !== 'loaded') return 50 | this.onreadystatechange = null 51 | for (const cb of callbacks) { 52 | cb(null, script) // there is no way to catch loading errors in IE8 53 | } 54 | callbacks = null 55 | } 56 | } 57 | } 58 | 59 | export default dynamicLoadScript 60 | -------------------------------------------------------------------------------- /fir_admin/src/components/Tinymce/plugins.js: -------------------------------------------------------------------------------- 1 | // Any plugins you want to use has to be imported 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ 4 | 5 | const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] 6 | 7 | export default plugins 8 | -------------------------------------------------------------------------------- /fir_admin/src/components/Tinymce/toolbar.js: -------------------------------------------------------------------------------- 1 | // Here is a list of the toolbar 2 | // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols 3 | 4 | const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] 5 | 6 | export default toolbar 7 | -------------------------------------------------------------------------------- /fir_admin/src/directive/clipboard/clipboard.js: -------------------------------------------------------------------------------- 1 | // Inspired by https://github.com/Inndy/vue-clipboard2 2 | const Clipboard = require('clipboard') 3 | if (!Clipboard) { 4 | throw new Error('you should npm install `clipboard` --save at first ') 5 | } 6 | 7 | export default { 8 | bind(el, binding) { 9 | if (binding.arg === 'success') { 10 | el._v_clipboard_success = binding.value 11 | } else if (binding.arg === 'error') { 12 | el._v_clipboard_error = binding.value 13 | } else { 14 | const clipboard = new Clipboard(el, { 15 | text() { return binding.value }, 16 | action() { return binding.arg === 'cut' ? 'cut' : 'copy' } 17 | }) 18 | clipboard.on('success', e => { 19 | const callback = el._v_clipboard_success 20 | callback && callback(e) // eslint-disable-line 21 | }) 22 | clipboard.on('error', e => { 23 | const callback = el._v_clipboard_error 24 | callback && callback(e) // eslint-disable-line 25 | }) 26 | el._v_clipboard = clipboard 27 | } 28 | }, 29 | update(el, binding) { 30 | if (binding.arg === 'success') { 31 | el._v_clipboard_success = binding.value 32 | } else if (binding.arg === 'error') { 33 | el._v_clipboard_error = binding.value 34 | } else { 35 | el._v_clipboard.text = function() { return binding.value } 36 | el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' } 37 | } 38 | }, 39 | unbind(el, binding) { 40 | if (binding.arg === 'success') { 41 | delete el._v_clipboard_success 42 | } else if (binding.arg === 'error') { 43 | delete el._v_clipboard_error 44 | } else { 45 | el._v_clipboard.destroy() 46 | delete el._v_clipboard 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /fir_admin/src/directive/clipboard/index.js: -------------------------------------------------------------------------------- 1 | import Clipboard from './clipboard' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('Clipboard', Clipboard) 5 | } 6 | 7 | if (window.Vue) { 8 | window.clipboard = Clipboard 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | Clipboard.install = install 13 | export default Clipboard 14 | -------------------------------------------------------------------------------- /fir_admin/src/directive/el-drag-dialog/index.js: -------------------------------------------------------------------------------- 1 | import drag from './drag' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-drag-dialog', drag) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-drag-dialog'] = drag 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | drag.install = install 13 | export default drag 14 | -------------------------------------------------------------------------------- /fir_admin/src/directive/el-table/adaptive.js: -------------------------------------------------------------------------------- 1 | import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event' 2 | 3 | /** 4 | * How to use 5 | * ... 6 | * el-table height is must be set 7 | * bottomOffset: 30(default) // The height of the table from the bottom of the page. 8 | */ 9 | 10 | const doResize = (el, binding, vnode) => { 11 | const { componentInstance: $table } = vnode 12 | 13 | const { value } = binding 14 | 15 | if (!$table.height) { 16 | throw new Error(`el-$table must set the height. Such as height='100px'`) 17 | } 18 | const bottomOffset = (value && value.bottomOffset) || 30 19 | 20 | if (!$table) return 21 | 22 | const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset 23 | $table.layout.setHeight(height) 24 | $table.doLayout() 25 | } 26 | 27 | export default { 28 | bind(el, binding, vnode) { 29 | el.resizeListener = () => { 30 | doResize(el, binding, vnode) 31 | } 32 | // parameter 1 is must be "Element" type 33 | addResizeListener(window.document.body, el.resizeListener) 34 | }, 35 | inserted(el, binding, vnode) { 36 | doResize(el, binding, vnode) 37 | }, 38 | unbind(el) { 39 | removeResizeListener(window.document.body, el.resizeListener) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fir_admin/src/directive/el-table/index.js: -------------------------------------------------------------------------------- 1 | import adaptive from './adaptive' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-height-adaptive-table', adaptive) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-height-adaptive-table'] = adaptive 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | adaptive.install = install 13 | export default adaptive 14 | -------------------------------------------------------------------------------- /fir_admin/src/directive/permission/index.js: -------------------------------------------------------------------------------- 1 | import permission from './permission' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('permission', permission) 5 | } 6 | 7 | if (window.Vue) { 8 | window['permission'] = permission 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | permission.install = install 13 | export default permission 14 | -------------------------------------------------------------------------------- /fir_admin/src/directive/permission/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | function checkPermission(el, binding) { 4 | const { value } = binding 5 | const roles = store.getters && store.getters.roles 6 | 7 | if (value && value instanceof Array) { 8 | if (value.length > 0) { 9 | const permissionRoles = value 10 | 11 | const hasPermission = roles.some(role => { 12 | return permissionRoles.includes(role) 13 | }) 14 | 15 | if (!hasPermission) { 16 | el.parentNode && el.parentNode.removeChild(el) 17 | } 18 | } 19 | } else { 20 | throw new Error(`need roles! Like v-permission="['admin','editor']"`) 21 | } 22 | } 23 | 24 | export default { 25 | inserted(el, binding) { 26 | checkPermission(el, binding) 27 | }, 28 | update(el, binding) { 29 | checkPermission(el, binding) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fir_admin/src/directive/waves/index.js: -------------------------------------------------------------------------------- 1 | import waves from './waves' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('waves', waves) 5 | } 6 | 7 | if (window.Vue) { 8 | window.waves = waves 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | waves.install = install 13 | export default waves 14 | -------------------------------------------------------------------------------- /fir_admin/src/directive/waves/waves.css: -------------------------------------------------------------------------------- 1 | .waves-ripple { 2 | position: absolute; 3 | border-radius: 100%; 4 | background-color: rgba(0, 0, 0, 0.15); 5 | background-clip: padding-box; 6 | pointer-events: none; 7 | -webkit-user-select: none; 8 | -moz-user-select: none; 9 | -ms-user-select: none; 10 | user-select: none; 11 | -webkit-transform: scale(0); 12 | -ms-transform: scale(0); 13 | transform: scale(0); 14 | opacity: 1; 15 | } 16 | 17 | .waves-ripple.z-active { 18 | opacity: 0; 19 | -webkit-transform: scale(2); 20 | -ms-transform: scale(2); 21 | transform: scale(2); 22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out; 25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; 26 | } -------------------------------------------------------------------------------- /fir_admin/src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg component 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fir_admin/src/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /fir_admin/src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 32 | 33 | 41 | -------------------------------------------------------------------------------- /fir_admin/src/layout/components/Sidebar/FixiOSBug.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | device() { 4 | return this.$store.state.app.device 5 | } 6 | }, 7 | mounted() { 8 | // In order to fix the click on menu on the ios device will trigger the mouseleave bug 9 | // https://github.com/PanJiaChen/vue-element-admin/issues/1135 10 | this.fixBugIniOS() 11 | }, 12 | methods: { 13 | fixBugIniOS() { 14 | const $subMenu = this.$refs.subMenu 15 | if ($subMenu) { 16 | const handleMouseleave = $subMenu.handleMouseleave 17 | $subMenu.handleMouseleave = (e) => { 18 | if (this.device === 'mobile') { 19 | return 20 | } 21 | handleMouseleave(e) 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /fir_admin/src/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 42 | -------------------------------------------------------------------------------- /fir_admin/src/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 44 | -------------------------------------------------------------------------------- /fir_admin/src/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 57 | -------------------------------------------------------------------------------- /fir_admin/src/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './Navbar' 2 | export { default as Sidebar } from './Sidebar' 3 | export { default as AppMain } from './AppMain' 4 | -------------------------------------------------------------------------------- /fir_admin/src/layout/mixin/ResizeHandler.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | const { body } = document 4 | const WIDTH = 992 // refer to Bootstrap's responsive design 5 | 6 | export default { 7 | watch: { 8 | $route(route) { 9 | if (this.device === 'mobile' && this.sidebar.opened) { 10 | store.dispatch('app/closeSideBar', { withoutAnimation: false }) 11 | } 12 | } 13 | }, 14 | beforeMount() { 15 | window.addEventListener('resize', this.$_resizeHandler) 16 | }, 17 | beforeDestroy() { 18 | window.removeEventListener('resize', this.$_resizeHandler) 19 | }, 20 | mounted() { 21 | const isMobile = this.$_isMobile() 22 | if (isMobile) { 23 | store.dispatch('app/toggleDevice', 'mobile') 24 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) 25 | } 26 | }, 27 | methods: { 28 | // use $_ for mixins properties 29 | // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential 30 | $_isMobile() { 31 | const rect = body.getBoundingClientRect() 32 | return rect.width - 1 < WIDTH 33 | }, 34 | $_resizeHandler() { 35 | if (!document.hidden) { 36 | const isMobile = this.$_isMobile() 37 | store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') 38 | 39 | if (isMobile) { 40 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /fir_admin/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import 'normalize.css/normalize.css' // A modern alternative to CSS resets 4 | 5 | import ElementUI from 'element-ui' 6 | import 'element-ui/lib/theme-chalk/index.css' 7 | // import locale from 'element-ui/lib/locale/lang/en' // lang i18n 8 | 9 | import '@/styles/index.scss' // global css 10 | 11 | import App from './App' 12 | import store from './store' 13 | import router from './router' 14 | 15 | import '@/icons' // icon 16 | import '@/permission' // permission control 17 | 18 | /** 19 | * If you don't want to use mock-server 20 | * you want to use MockJs for mock api 21 | * you can execute: mockXHR() 22 | * 23 | * Currently MockJs will be used in the production environment, 24 | * please remove it before going online ! ! ! 25 | */ 26 | if (process.env.NODE_ENV === 'production') { 27 | const { mockXHR } = require('../mock') 28 | mockXHR() 29 | } 30 | 31 | // set ElementUI lang to EN 32 | // Vue.use(ElementUI, { locale }) 33 | // 如果想要中文版 element-ui,按如下方式声明 34 | Vue.use(ElementUI) 35 | 36 | Vue.config.productionTip = false 37 | 38 | new Vue({ 39 | el: '#app', 40 | router, 41 | store, 42 | render: h => h(App) 43 | }) 44 | -------------------------------------------------------------------------------- /fir_admin/src/permission.js: -------------------------------------------------------------------------------- 1 | import router from './router' 2 | import store from './store' 3 | import { Message } from 'element-ui' 4 | import NProgress from 'nprogress' // progress bar 5 | import 'nprogress/nprogress.css' // progress bar style 6 | import { getToken } from '@/utils/auth' // get token from cookie 7 | import getPageTitle from '@/utils/get-page-title' 8 | 9 | NProgress.configure({ showSpinner: false }) // NProgress Configuration 10 | 11 | const whiteList = ['/login'] // no redirect whitelist 12 | 13 | router.beforeEach(async(to, from, next) => { 14 | // start progress bar 15 | NProgress.start() 16 | 17 | // set page title 18 | document.title = getPageTitle(to.meta.title) 19 | 20 | // determine whether the user has logged in 21 | const hasToken = getToken() 22 | 23 | if (hasToken) { 24 | if (to.path === '/login') { 25 | // if is logged in, redirect to the home page 26 | next({ path: '/' }) 27 | NProgress.done() 28 | } else { 29 | const hasGetUserInfo = store.getters.name 30 | if (hasGetUserInfo) { 31 | next() 32 | } else { 33 | try { 34 | // get user info 35 | await store.dispatch('user/getInfo') 36 | 37 | next() 38 | } catch (error) { 39 | // remove token and go to login page to re-login 40 | await store.dispatch('user/resetToken') 41 | Message.error(error || 'Has Error') 42 | next(`/login?redirect=${to.path}`) 43 | NProgress.done() 44 | } 45 | } 46 | } 47 | } else { 48 | /* has no token*/ 49 | 50 | if (whiteList.indexOf(to.path) !== -1) { 51 | // in the free login whitelist, go directly 52 | next() 53 | } else { 54 | // other pages that do not have permission to access are redirected to the login page. 55 | next(`/login?redirect=${to.path}`) 56 | NProgress.done() 57 | } 58 | } 59 | }) 60 | 61 | router.afterEach(() => { 62 | // finish progress bar 63 | NProgress.done() 64 | }) 65 | -------------------------------------------------------------------------------- /fir_admin/src/settings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | title: 'FLY 应用分发平台 管理后台', 4 | 5 | /** 6 | * @type {boolean} true | false 7 | * @description Whether fix the header 8 | */ 9 | fixedHeader: false, 10 | 11 | /** 12 | * @type {boolean} true | false 13 | * @description Whether show the logo in sidebar 14 | */ 15 | sidebarLogo: false 16 | } 17 | -------------------------------------------------------------------------------- /fir_admin/src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | sidebar: state => state.app.sidebar, 3 | device: state => state.app.device, 4 | token: state => state.user.token, 5 | avatar: state => state.user.avatar, 6 | name: state => state.user.name 7 | } 8 | export default getters 9 | -------------------------------------------------------------------------------- /fir_admin/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import getters from './getters' 4 | import app from './modules/app' 5 | import settings from './modules/settings' 6 | import user from './modules/user' 7 | 8 | Vue.use(Vuex) 9 | 10 | const store = new Vuex.Store({ 11 | modules: { 12 | app, 13 | settings, 14 | user 15 | }, 16 | getters 17 | }) 18 | 19 | export default store 20 | -------------------------------------------------------------------------------- /fir_admin/src/store/modules/app.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const state = { 4 | sidebar: { 5 | opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, 6 | withoutAnimation: false 7 | }, 8 | device: 'desktop' 9 | } 10 | 11 | const mutations = { 12 | TOGGLE_SIDEBAR: state => { 13 | state.sidebar.opened = !state.sidebar.opened 14 | state.sidebar.withoutAnimation = false 15 | if (state.sidebar.opened) { 16 | Cookies.set('sidebarStatus', 1) 17 | } else { 18 | Cookies.set('sidebarStatus', 0) 19 | } 20 | }, 21 | CLOSE_SIDEBAR: (state, withoutAnimation) => { 22 | Cookies.set('sidebarStatus', 0) 23 | state.sidebar.opened = false 24 | state.sidebar.withoutAnimation = withoutAnimation 25 | }, 26 | TOGGLE_DEVICE: (state, device) => { 27 | state.device = device 28 | } 29 | } 30 | 31 | const actions = { 32 | toggleSideBar({ commit }) { 33 | commit('TOGGLE_SIDEBAR') 34 | }, 35 | closeSideBar({ commit }, { withoutAnimation }) { 36 | commit('CLOSE_SIDEBAR', withoutAnimation) 37 | }, 38 | toggleDevice({ commit }, device) { 39 | commit('TOGGLE_DEVICE', device) 40 | } 41 | } 42 | 43 | export default { 44 | namespaced: true, 45 | state, 46 | mutations, 47 | actions 48 | } 49 | -------------------------------------------------------------------------------- /fir_admin/src/store/modules/settings.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const { showSettings, fixedHeader, sidebarLogo } = defaultSettings 4 | 5 | const state = { 6 | showSettings: showSettings, 7 | fixedHeader: fixedHeader, 8 | sidebarLogo: sidebarLogo 9 | } 10 | 11 | const mutations = { 12 | CHANGE_SETTING: (state, { key, value }) => { 13 | // eslint-disable-next-line no-prototype-builtins 14 | if (state.hasOwnProperty(key)) { 15 | state[key] = value 16 | } 17 | } 18 | } 19 | 20 | const actions = { 21 | changeSetting({ commit }, data) { 22 | commit('CHANGE_SETTING', data) 23 | } 24 | } 25 | 26 | export default { 27 | namespaced: true, 28 | state, 29 | mutations, 30 | actions 31 | } 32 | 33 | -------------------------------------------------------------------------------- /fir_admin/src/styles/element-ui.scss: -------------------------------------------------------------------------------- 1 | // cover some element-ui styles 2 | 3 | .el-breadcrumb__inner, 4 | .el-breadcrumb__inner a { 5 | font-weight: 400 !important; 6 | } 7 | 8 | .el-upload { 9 | input[type="file"] { 10 | display: none !important; 11 | } 12 | } 13 | 14 | .el-upload__input { 15 | display: none; 16 | } 17 | 18 | 19 | // to fixed https://github.com/ElemeFE/element/issues/2461 20 | .el-dialog { 21 | transform: none; 22 | left: 0; 23 | position: relative; 24 | margin: 0 auto; 25 | } 26 | 27 | // refine element ui upload 28 | .upload-container { 29 | .el-upload { 30 | width: 100%; 31 | 32 | .el-upload-dragger { 33 | width: 100%; 34 | height: 200px; 35 | } 36 | } 37 | } 38 | 39 | // dropdown 40 | .el-dropdown-menu { 41 | a { 42 | display: block 43 | } 44 | } 45 | 46 | // to fix el-date-picker css style 47 | .el-range-separator { 48 | box-sizing: content-box; 49 | } 50 | -------------------------------------------------------------------------------- /fir_admin/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | @import './mixin.scss'; 3 | @import './transition.scss'; 4 | @import './element-ui.scss'; 5 | @import './sidebar.scss'; 6 | 7 | body { 8 | height: 100%; 9 | -moz-osx-font-smoothing: grayscale; 10 | -webkit-font-smoothing: antialiased; 11 | text-rendering: optimizeLegibility; 12 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 13 | } 14 | 15 | label { 16 | font-weight: 700; 17 | } 18 | 19 | html { 20 | height: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | #app { 25 | height: 100%; 26 | } 27 | 28 | *, 29 | *:before, 30 | *:after { 31 | box-sizing: inherit; 32 | } 33 | 34 | a:focus, 35 | a:active { 36 | outline: none; 37 | } 38 | 39 | a, 40 | a:focus, 41 | a:hover { 42 | cursor: pointer; 43 | color: inherit; 44 | text-decoration: none; 45 | } 46 | 47 | div:focus { 48 | outline: none; 49 | } 50 | 51 | .clearfix { 52 | &:after { 53 | visibility: hidden; 54 | display: block; 55 | font-size: 0; 56 | content: " "; 57 | clear: both; 58 | height: 0; 59 | } 60 | } 61 | 62 | // main-container global css 63 | .app-container { 64 | padding: 20px; 65 | } 66 | 67 | .filter-container { 68 | padding-bottom: 10px; 69 | 70 | .filter-item { 71 | display: inline-block; 72 | vertical-align: middle; 73 | margin-bottom: 10px; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /fir_admin/src/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | @mixin scrollBar { 10 | &::-webkit-scrollbar-track-piece { 11 | background: #d3dce6; 12 | } 13 | 14 | &::-webkit-scrollbar { 15 | width: 6px; 16 | } 17 | 18 | &::-webkit-scrollbar-thumb { 19 | background: #99a9bf; 20 | border-radius: 20px; 21 | } 22 | } 23 | 24 | @mixin relative { 25 | position: relative; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | -------------------------------------------------------------------------------- /fir_admin/src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // global transition css 2 | 3 | /* fade */ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /* fade-transform */ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .5s; 18 | } 19 | 20 | .fade-transform-enter { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /* breadcrumb transition */ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .5s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .5s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } 49 | -------------------------------------------------------------------------------- /fir_admin/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // sidebar 2 | $menuText:#bfcbd9; 3 | $menuActiveText:#409EFF; 4 | $subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951 5 | 6 | $menuBg:#304156; 7 | $menuHover:#263445; 8 | 9 | $subMenuBg:#1f2d3d; 10 | $subMenuHover:#001528; 11 | 12 | $sideBarWidth: 210px; 13 | 14 | // the :export directive is the magic sauce for webpack 15 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 16 | :export { 17 | menuText: $menuText; 18 | menuActiveText: $menuActiveText; 19 | subMenuActiveText: $subMenuActiveText; 20 | menuBg: $menuBg; 21 | menuHover: $menuHover; 22 | subMenuBg: $subMenuBg; 23 | subMenuHover: $subMenuHover; 24 | sideBarWidth: $sideBarWidth; 25 | } 26 | -------------------------------------------------------------------------------- /fir_admin/src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'vue_admin_template_token' 4 | 5 | export function getToken() { 6 | return Cookies.get(TokenKey) 7 | } 8 | 9 | export function setToken(token) { 10 | return Cookies.set(TokenKey, token) 11 | } 12 | 13 | export function removeToken() { 14 | return Cookies.remove(TokenKey) 15 | } 16 | -------------------------------------------------------------------------------- /fir_admin/src/utils/get-page-title.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const title = defaultSettings.title || 'Vue Admin Template' 4 | 5 | export default function getPageTitle(pageTitle) { 6 | if (pageTitle) { 7 | return `${pageTitle} - ${title}` 8 | } 9 | return `${title}` 10 | } 11 | -------------------------------------------------------------------------------- /fir_admin/src/utils/scroll-to.js: -------------------------------------------------------------------------------- 1 | Math.easeInOutQuad = function(t, b, c, d) { 2 | t /= d / 2 3 | if (t < 1) { 4 | return c / 2 * t * t + b 5 | } 6 | t-- 7 | return -c / 2 * (t * (t - 2) - 1) + b 8 | } 9 | 10 | // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts 11 | var requestAnimFrame = (function() { 12 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } 13 | })() 14 | 15 | /** 16 | * Because it's so fucking difficult to detect the scrolling element, just move them all 17 | * @param {number} amount 18 | */ 19 | function move(amount) { 20 | document.documentElement.scrollTop = amount 21 | document.body.parentNode.scrollTop = amount 22 | document.body.scrollTop = amount 23 | } 24 | 25 | function position() { 26 | return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop 27 | } 28 | 29 | /** 30 | * @param {number} to 31 | * @param {number} duration 32 | * @param {Function} callback 33 | */ 34 | export function scrollTo(to, duration, callback) { 35 | const start = position() 36 | const change = to - start 37 | const increment = 20 38 | let currentTime = 0 39 | duration = (typeof (duration) === 'undefined') ? 500 : duration 40 | var animateScroll = function() { 41 | // increment the time 42 | currentTime += increment 43 | // find the value with the quadratic in-out easing function 44 | var val = Math.easeInOutQuad(currentTime, start, change, duration) 45 | // move the document.body 46 | move(val) 47 | // do the animation unless its over 48 | if (currentTime < duration) { 49 | requestAnimFrame(animateScroll) 50 | } else { 51 | if (callback && typeof (callback) === 'function') { 52 | // the animation is done so lets callback 53 | callback() 54 | } 55 | } 56 | } 57 | animateScroll() 58 | } 59 | -------------------------------------------------------------------------------- /fir_admin/src/views/appinfos/edit.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /fir_admin/src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 31 | -------------------------------------------------------------------------------- /fir_admin/src/views/nested/menu1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /fir_admin/src/views/nested/menu1/menu1-1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /fir_admin/src/views/nested/menu1/menu1-2/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /fir_admin/src/views/nested/menu1/menu1-2/menu1-2-1/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /fir_admin/src/views/nested/menu1/menu1-2/menu1-2-2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /fir_admin/src/views/nested/menu1/menu1-3/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /fir_admin/src/views/nested/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /fir_admin/src/views/supersign/index.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /fir_admin/src/views/tree/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 78 | 79 | -------------------------------------------------------------------------------- /fir_admin/src/views/userinfos/Dropdown/Comment.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 42 | -------------------------------------------------------------------------------- /fir_admin/src/views/userinfos/Dropdown/Platform.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 47 | -------------------------------------------------------------------------------- /fir_admin/src/views/userinfos/Dropdown/SourceUrl.vue: -------------------------------------------------------------------------------- 1 | v 18 | 19 | 39 | -------------------------------------------------------------------------------- /fir_admin/src/views/userinfos/Dropdown/index.js: -------------------------------------------------------------------------------- 1 | export { default as CommentDropdown } from './Comment' 2 | export { default as PlatformDropdown } from './Platform' 3 | export { default as SourceUrlDropdown } from './SourceUrl' 4 | -------------------------------------------------------------------------------- /fir_admin/src/views/userinfos/edit.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /fir_client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /fir_client/README.md: -------------------------------------------------------------------------------- 1 | # fir_test 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | yarn lint 21 | ``` 22 | ### mshort 和 short 区别 23 | 区别就是引用的下载不同,mshort 下面也引用了elemet , short 没有引用,mshort最终包400kb 左右,short 130kb ,体积相差3倍左右 24 | 25 | ### deploy nginx 26 | ``` 27 | location ~ ^/(index|apps|user|login|register|supersign) { 28 | try_files $uri $uri/ /index.html; 29 | } 30 | 31 | # location / { 32 | # try_files $uri $uri/ /mshort.html; 33 | # } 34 | 35 | location / { 36 | try_files $uri $uri/ /short.html; 37 | } 38 | 39 | ``` 40 | 41 | 42 | ## deploy nginx 43 | 44 | #### short deploy nginx 45 | ```shell 46 | location / { 47 | try_files $uri $uri/ /short.html; 48 | } 49 | ``` 50 | #### index.html 51 | ```html 52 | 53 | 54 | 55 | 56 | 57 | FLY分发平台 58 | 79 | 80 | 81 |
82 |

这是分发平台下载默认页,正在跳转 首页

83 |
84 | 85 | 86 | ``` 87 | -------------------------------------------------------------------------------- /fir_client/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | "@babel/preset-env", 5 | ], 6 | "plugins": [ 7 | [ 8 | "component", 9 | { 10 | "libraryName": "element-ui", 11 | "styleLibraryName": "theme-chalk" 12 | } 13 | ] 14 | ] 15 | }; -------------------------------------------------------------------------------- /fir_client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fir_client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "analyz": "vue-cli-service serve analyz", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "ali-oss": "^6.5.1", 13 | "app-info-parser": "^0.3.9", 14 | "axios": "^0.22.0", 15 | "core-js": "^3.4.4", 16 | "element-ui": "^2.13.0", 17 | "js-base64": "^2.5.2", 18 | "qiniu-js": "^2.5.5", 19 | "qrcodejs2": "^0.0.2", 20 | "vue": "^2.6.10", 21 | "vue-clipboard2": "^0.3.1", 22 | "vue-cookies": "^1.7.0", 23 | "vue-cropper": "^0.5.7", 24 | "vue-lazyload": "^1.3.3", 25 | "vue-qr": "^2.3.0", 26 | "vue-router": "^3.1.3", 27 | "vuex": "^3.1.2" 28 | }, 29 | "devDependencies": { 30 | "@vue/cli-plugin-babel": "^4.1.0", 31 | "@vue/cli-plugin-eslint": "^4.1.0", 32 | "@vue/cli-service": "^4.1.0", 33 | "babel-eslint": "^10.0.3", 34 | "babel-plugin-component": "^1.1.1", 35 | "compression-webpack-plugin": "5.0.1", 36 | "eslint": "^5.16.0", 37 | "eslint-plugin-vue": "^5.0.0", 38 | "html-webpack-plugin": "^5.3.1", 39 | "script-ext-html-webpack-plugin": "^2.1.5", 40 | "uglifyjs-webpack-plugin": "^2.2.0", 41 | "vue-template-compiler": "^2.6.10", 42 | "webpack-bundle-analyzer": "^4.4.1" 43 | }, 44 | "eslintConfig": { 45 | "root": true, 46 | "env": { 47 | "node": true 48 | }, 49 | "extends": [ 50 | "plugin:vue/essential", 51 | "eslint:recommended" 52 | ], 53 | "rules": {}, 54 | "parserOptions": { 55 | "parser": "babel-eslint" 56 | } 57 | }, 58 | "browserslist": [ 59 | "> 1%", 60 | "last 2 versions" 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /fir_client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/public/favicon.ico -------------------------------------------------------------------------------- /fir_client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | FLY分发平台 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fir_client/public/short.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 应用下载 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fir_client/src/assets/beianlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/beianlogo.png -------------------------------------------------------------------------------- /fir_client/src/assets/bg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/bg.jpeg -------------------------------------------------------------------------------- /fir_client/src/assets/download_pattern_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/download_pattern_left.png -------------------------------------------------------------------------------- /fir_client/src/assets/download_pattern_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/download_pattern_right.png -------------------------------------------------------------------------------- /fir_client/src/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/github.png -------------------------------------------------------------------------------- /fir_client/src/assets/icon/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/icon/iconfont.eot -------------------------------------------------------------------------------- /fir_client/src/assets/icon/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "", 3 | "name": "", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon-", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "217325", 10 | "name": "android", 11 | "font_class": "android", 12 | "unicode": "e61d", 13 | "unicode_decimal": 58909 14 | }, 15 | { 16 | "icon_id": "1261088", 17 | "name": "android", 18 | "font_class": "android1", 19 | "unicode": "e616", 20 | "unicode_decimal": 58902 21 | }, 22 | { 23 | "icon_id": "1410210", 24 | "name": "android", 25 | "font_class": "android2", 26 | "unicode": "e62a", 27 | "unicode_decimal": 58922 28 | }, 29 | { 30 | "icon_id": "1420800", 31 | "name": "IOS", 32 | "font_class": "ios", 33 | "unicode": "e60c", 34 | "unicode_decimal": 58892 35 | }, 36 | { 37 | "icon_id": "1546553", 38 | "name": "IOS", 39 | "font_class": "IOS", 40 | "unicode": "e68c", 41 | "unicode_decimal": 59020 42 | }, 43 | { 44 | "icon_id": "2075064", 45 | "name": "ios", 46 | "font_class": "ios1", 47 | "unicode": "e601", 48 | "unicode_decimal": 58881 49 | }, 50 | { 51 | "icon_id": "2325952", 52 | "name": "ios", 53 | "font_class": "ios2", 54 | "unicode": "e64b", 55 | "unicode_decimal": 58955 56 | }, 57 | { 58 | "icon_id": "3965518", 59 | "name": "Android", 60 | "font_class": "Android", 61 | "unicode": "e6d4", 62 | "unicode_decimal": 59092 63 | }, 64 | { 65 | "icon_id": "13302922", 66 | "name": "android", 67 | "font_class": "android3", 68 | "unicode": "e625", 69 | "unicode_decimal": 58917 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /fir_client/src/assets/icon/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/icon/iconfont.ttf -------------------------------------------------------------------------------- /fir_client/src/assets/icon/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/icon/iconfont.woff -------------------------------------------------------------------------------- /fir_client/src/assets/icon/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/icon/iconfont.woff2 -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/1.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/2.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/3.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/5.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/6.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/7.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/8.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/9.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/banner1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/banner1.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/banner2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/banner2.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/banner_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/banner_1.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/banner_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/banner_2.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/circle.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/computer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/computer.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/contact_us_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/contact_us_bg.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/qq.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/service1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/service1.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/service2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/service2.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/service3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/service3.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/service4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/service4.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/skill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/skill.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/tel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/tel.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/twitter.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/weibo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/weibo.png -------------------------------------------------------------------------------- /fir_client/src/assets/imgs/weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/imgs/weixin.png -------------------------------------------------------------------------------- /fir_client/src/assets/inhouse/b1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/inhouse/b1.png -------------------------------------------------------------------------------- /fir_client/src/assets/inhouse/b2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/inhouse/b2.png -------------------------------------------------------------------------------- /fir_client/src/assets/inhouse/b3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/inhouse/b3.png -------------------------------------------------------------------------------- /fir_client/src/assets/inhouse/b4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/inhouse/b4.png -------------------------------------------------------------------------------- /fir_client/src/assets/inhouse/b5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/inhouse/b5.png -------------------------------------------------------------------------------- /fir_client/src/assets/inhouse/b6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/inhouse/b6.png -------------------------------------------------------------------------------- /fir_client/src/assets/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/loading.gif -------------------------------------------------------------------------------- /fir_client/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/logo.png -------------------------------------------------------------------------------- /fir_client/src/assets/pay/order-weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/pay/order-weixin.png -------------------------------------------------------------------------------- /fir_client/src/assets/pay/pay-scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/pay/pay-scan.png -------------------------------------------------------------------------------- /fir_client/src/assets/pay/pay_alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/pay/pay_alipay.png -------------------------------------------------------------------------------- /fir_client/src/assets/pay/pay_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/pay/pay_selected.png -------------------------------------------------------------------------------- /fir_client/src/assets/pay/pay_weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/pay/pay_weixin.png -------------------------------------------------------------------------------- /fir_client/src/assets/sign/help/sign_help_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/help/sign_help_0.png -------------------------------------------------------------------------------- /fir_client/src/assets/sign/help/sign_help_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/help/sign_help_1.png -------------------------------------------------------------------------------- /fir_client/src/assets/sign/help/sign_help_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/help/sign_help_2.png -------------------------------------------------------------------------------- /fir_client/src/assets/sign/help/sign_help_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/help/sign_help_3.png -------------------------------------------------------------------------------- /fir_client/src/assets/sign/help/sign_help_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/help/sign_help_4.png -------------------------------------------------------------------------------- /fir_client/src/assets/sign/help/sign_help_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/help/sign_help_5.png -------------------------------------------------------------------------------- /fir_client/src/assets/sign/step1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/step1.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/sign/step2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/step2.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/sign/step3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/step3.jpg -------------------------------------------------------------------------------- /fir_client/src/assets/sign/step4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_client/src/assets/sign/step4.jpg -------------------------------------------------------------------------------- /fir_client/src/components/FirFooter.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 41 | 42 | 52 | -------------------------------------------------------------------------------- /fir_client/src/components/apps/FirAppInfosdevices.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 58 | 59 | 62 | -------------------------------------------------------------------------------- /fir_client/src/components/index/FirContact.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 43 | 44 | 75 | -------------------------------------------------------------------------------- /fir_client/src/components/user/FirSuperSignHelp.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 31 | 32 | 44 | -------------------------------------------------------------------------------- /fir_client/src/main.short.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Download from "@/Download"; 3 | import router from "@/router/download"; 4 | 5 | import 'element-ui/lib/theme-chalk/index.css' 6 | import { 7 | Button, 8 | Col, 9 | Container, 10 | Divider, 11 | Form, 12 | FormItem, 13 | Header, 14 | Input, 15 | Link, 16 | Main, 17 | Message, 18 | Notification, 19 | Option, 20 | Row, 21 | Select 22 | } from "element-ui"; 23 | import VueLazyload from 'vue-lazyload' 24 | 25 | Vue.use(Link); 26 | Vue.use(Input); 27 | Vue.use(Button); 28 | Vue.use(Container); 29 | Vue.use(Main); 30 | Vue.use(Divider); 31 | Vue.use(Select); 32 | Vue.use(Option); 33 | Vue.use(Header); 34 | Vue.use(Form); 35 | Vue.use(FormItem); 36 | Vue.use(Row); 37 | Vue.use(Col); 38 | Vue.prototype.$message = Message; 39 | Vue.prototype.$notify = Notification; 40 | 41 | 42 | Vue.config.productionTip = false; 43 | 44 | Vue.use(VueLazyload, { 45 | loading: require('./assets/loading.gif'), 46 | preLoad: 1 47 | }); 48 | 49 | new Vue({ 50 | render: h => h(Download), 51 | router, 52 | }).$mount('#download'); 53 | -------------------------------------------------------------------------------- /fir_client/src/router/download.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | //1.先导入 3 | import VueRouter from 'vue-router' 4 | //2.一定先use 一下 5 | Vue.use(VueRouter); 6 | 7 | // 解决push 同一个路由的错误 8 | const originalPush = VueRouter.prototype.push; 9 | VueRouter.prototype.push = function push(location) { 10 | return originalPush.call(this, location).catch(err => err) 11 | }; 12 | 13 | // import FirDownload from "@/components/FirDownload"; 14 | const router = new VueRouter({ 15 | mode: 'history', 16 | routes: [ 17 | { 18 | path: '/:short', 19 | name: 'FirDownload', 20 | // component: () => import("@/components/ShortDownload") 21 | component: () => import("@/components/FirDownload") 22 | } 23 | ] 24 | }); 25 | 26 | export default router; 27 | -------------------------------------------------------------------------------- /fir_client/src/router/short.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | //1.先导入 3 | import VueRouter from 'vue-router' 4 | //2.一定先use 一下 5 | Vue.use(VueRouter); 6 | 7 | // 解决push 同一个路由的错误 8 | const originalPush = VueRouter.prototype.push; 9 | VueRouter.prototype.push = function push(location) { 10 | return originalPush.call(this, location).catch(err => err) 11 | }; 12 | 13 | // import FirDownload from "@/components/FirDownload"; 14 | const router = new VueRouter({ 15 | mode: 'history', 16 | routes: [ 17 | { 18 | path: '/:short', 19 | name: 'FirDownload', 20 | component: () => import("@/components/ShortDownload") 21 | // component: () => import("@/components/FirDownload") 22 | } 23 | ] 24 | }); 25 | 26 | export default router; 27 | -------------------------------------------------------------------------------- /fir_client/src/short.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Download from "@/Download"; 3 | import router from "@/router/short"; 4 | 5 | import VueLazyload from 'vue-lazyload' 6 | 7 | Vue.use(VueLazyload, { 8 | loading: require('./assets/loading.gif'), 9 | preLoad: 1 10 | }); 11 | Vue.config.productionTip = false; 12 | 13 | new Vue({ 14 | render: h => h(Download), 15 | router, 16 | }).$mount('#download'); 17 | -------------------------------------------------------------------------------- /fir_client/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | /* 3 | 1、导入对象 4 | 2.Vue.use() 5 | 3.创建store对象 6 | 4.挂载 7 | 8 | */ 9 | import Vuex from 'vuex' 10 | 11 | Vue.use(Vuex); 12 | const store = new Vuex.Store({ 13 | state: { 14 | userinfo: {}, 15 | currentapp: {}, 16 | appInfoIndex: [], 17 | userInfoIndex: 0, 18 | show_domain_msg: false, 19 | domain_action: false, 20 | domain_show_state: false, 21 | }, 22 | mutations: { 23 | setuserinfo(state, data) { 24 | state.userinfo = data; 25 | }, 26 | setappInfoIndex(state, val) { 27 | state.appInfoIndex = val 28 | }, 29 | setuserInfoIndex(state, val) { 30 | state.userInfoIndex = val 31 | }, 32 | setcurrentapp(state, val) { 33 | state.currentapp = val 34 | }, 35 | setdomainshow(state, val) { 36 | state.show_domain_msg = val 37 | }, 38 | setdomainaction(state, val) { 39 | state.domain_action = val 40 | }, 41 | setdomainstate(state, val) { 42 | state.domain_show_state = val 43 | } 44 | }, 45 | actions: { 46 | dosetdomainstate(context, data) { 47 | context.commit('setdomainstate', data); 48 | }, 49 | dodomainaction(context, data) { 50 | context.commit('setdomainaction', data); 51 | }, 52 | dodomainshow(context, data) { 53 | context.commit('setdomainshow', data); 54 | }, 55 | doUserinfo(context, data) { 56 | context.commit('setuserinfo', data); 57 | }, 58 | doappInfoIndex(context, val) { 59 | context.commit('setappInfoIndex', val) 60 | }, 61 | douserInfoIndex(context, val) { 62 | context.commit('setuserInfoIndex', val) 63 | }, 64 | doucurrentapp(context, val) { 65 | context.commit('setcurrentapp', val) 66 | }, 67 | } 68 | }); 69 | 70 | 71 | export default store; 72 | -------------------------------------------------------------------------------- /fir_client/src/utils/format.js: -------------------------------------------------------------------------------- 1 | module.exports =function dateFormat(format, date=null) { 2 | if(!date){ 3 | date = new Date() 4 | } 5 | const args = { 6 | "M+": date.getMonth() + 1, 7 | "d+": date.getDate(), 8 | "h+": date.getHours(), 9 | "m+": date.getMinutes(), 10 | "s+": date.getSeconds(), 11 | "q+": Math.floor((date.getMonth() + 3) / 3), 12 | "S": date.getMilliseconds() 13 | }; 14 | if (/(y+)/.test(format)) 15 | format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); 16 | for (let i in args) { 17 | let n = args[i]; 18 | if (new RegExp("(" + i + ")").test(format)) 19 | format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? n : ("00" + n).substr(("" + n).length)); 20 | } 21 | return format; 22 | } 23 | -------------------------------------------------------------------------------- /fir_ser/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.14-slim 2 | 3 | # Fixes some weird terminal issues such as broken clear / CTRL+L 4 | 5 | #RUN sed -i 's/deb.debian.org/mirrors.163.com/g' /etc/apt/sources.list \ 6 | # && sed -i 's/security.debian.org/mirrors.163.com/g' /etc/apt/sources.list \ 7 | RUN apt update \ 8 | && apt-get install g++ wget zip unzip -y \ 9 | && apt-get install libssl-dev openssl libmariadb-dev -y \ 10 | && apt-get clean \ 11 | && rm -rf /var/lib/apt/lists/* 12 | 13 | 14 | #RUN cd /opt/ && wget https://github.com/nineaiyu/zsign/archive/refs/tags/v1.1.2.tar.gz 15 | ADD zsign-1.1.2.tar.gz /opt/ 16 | RUN cd /opt/zsign-1.1.2/ && g++ *.cpp common/*.cpp -lcrypto -O3 -std=c++11 -o zsign && cp zsign /usr/bin/ && rm -rf /opt/zsign-1.1.2/ 17 | # install pip 18 | COPY requirements.txt /opt/requirements.txt 19 | RUN cd /opt/ && pip install -U setuptools pip --ignore-installed && pip install --no-cache-dir -r requirements.txt && pip install --no-cache-dir uwsgi 20 | 21 | 22 | WORKDIR /data/fir_ser/ 23 | COPY entrypoint.sh entrypoint.sh 24 | RUN addgroup --system --gid 101 nginx \ 25 | && adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx 26 | 27 | #EXPOSE 443 28 | #ENTRYPOINT ["./entrypoint.sh"] 29 | #ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf", "-n"] 30 | #CMD ["python", "manage.py", "start", "all","-u","nginx","-usm","1"] 31 | ENTRYPOINT ["/bin/bash", "entrypoint.sh"] 32 | 33 | -------------------------------------------------------------------------------- /fir_ser/Dockerfile.cli: -------------------------------------------------------------------------------- 1 | FROM python:3.9.14-slim 2 | 3 | RUN pip install --upgrade pip --no-cache-dir && \ 4 | pip install setuptools-rust oss2 requests-toolbelt androguard requests --no-cache-dir 5 | 6 | WORKDIR /opt/ 7 | 8 | COPY cli.py /opt/cli.py 9 | 10 | CMD ["python", "cli.py"] 11 | 12 | -------------------------------------------------------------------------------- /fir_ser/admin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/admin/__init__.py -------------------------------------------------------------------------------- /fir_ser/admin/admin.py: -------------------------------------------------------------------------------- 1 | # Register your models here. 2 | -------------------------------------------------------------------------------- /fir_ser/admin/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AdminConfig(AppConfig): 5 | name = 'admin' 6 | -------------------------------------------------------------------------------- /fir_ser/admin/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/admin/migrations/__init__.py -------------------------------------------------------------------------------- /fir_ser/admin/models.py: -------------------------------------------------------------------------------- 1 | # Create your models here. 2 | -------------------------------------------------------------------------------- /fir_ser/admin/tests.py: -------------------------------------------------------------------------------- 1 | # Create your tests here. 2 | -------------------------------------------------------------------------------- /fir_ser/admin/utils/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 1月 4 | # author: NinEveN 5 | # date: 2022/1/13 6 | -------------------------------------------------------------------------------- /fir_ser/admin/utils/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 1月 4 | # author: NinEveN 5 | # date: 2022/1/6 6 | from rest_framework.pagination import PageNumberPagination 7 | from rest_framework.viewsets import ModelViewSet 8 | 9 | from common.core.response import ApiResponse 10 | 11 | 12 | class AppsPageNumber(PageNumberPagination): 13 | page_size = 20 # 每页显示多少条 14 | page_size_query_param = 'limit' # URL中每页显示条数的参数 15 | page_query_param = 'page' # URL中页码的参数 16 | max_page_size = 100 # 最大页码数限制 17 | 18 | 19 | class BaseModelSet(ModelViewSet): 20 | def retrieve(self, request, *args, **kwargs): 21 | data = super().retrieve(request, *args, **kwargs).data 22 | return ApiResponse(data=data) 23 | 24 | def list(self, request, *args, **kwargs): 25 | data = super().list(request, *args, **kwargs).data 26 | return ApiResponse(data=data) 27 | 28 | def destroy(self, request, *args, **kwargs): 29 | instance = self.get_object() 30 | self.perform_destroy(instance) 31 | return ApiResponse() 32 | 33 | def update(self, request, *args, **kwargs): 34 | data = super().update(request, *args, **kwargs).data 35 | return ApiResponse(data=data) 36 | -------------------------------------------------------------------------------- /fir_ser/admin/views/celery_flower.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | from django.conf import settings 4 | from django.http import HttpResponse 5 | from django.utils.translation import gettext as _ 6 | from proxy.views import proxy_view 7 | from rest_framework.views import APIView 8 | 9 | from common.core.auth import AdminTokenAuthentication 10 | 11 | flower_url = f'{settings.CELERY_FLOWER_HOST}:{settings.CELERY_FLOWER_PORT}' 12 | 13 | 14 | class CeleryFlowerView(APIView): 15 | if not settings.DEBUG: 16 | authentication_classes = [AdminTokenAuthentication, ] 17 | 18 | def get(self, request, path): 19 | remote_url = 'http://{}/flower/{}'.format(flower_url, path) 20 | try: 21 | response = proxy_view(request, remote_url) 22 | except Exception as e: 23 | msg = _("

Flower service unavailable, check it

") + \ 24 | '

{}
'.format(e) 25 | response = HttpResponse(msg) 26 | return response 27 | 28 | def post(self, request, path): 29 | return self.get(request, path) 30 | -------------------------------------------------------------------------------- /fir_ser/admin/views/domain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 4月 4 | # author: liuyu 5 | # date: 2021/4/11 6 | 7 | import logging 8 | 9 | from django_filters import rest_framework as filters 10 | from django_filters.rest_framework import DjangoFilterBackend 11 | from rest_framework.filters import OrderingFilter 12 | 13 | from admin.utils.serializer import AdminDomainNameSerializer 14 | from admin.utils.utils import BaseModelSet, AppsPageNumber 15 | from api.models import UserDomainInfo 16 | from common.core.auth import AdminTokenAuthentication 17 | from common.core.response import ApiResponse 18 | from common.utils.caches import reset_app_wx_easy_type 19 | 20 | logger = logging.getLogger(__name__) 21 | 22 | 23 | class DomainNameFilter(filters.FilterSet): 24 | app_name = filters.CharFilter(field_name="app_id__name", lookup_expr='icontains') 25 | 26 | class Meta: 27 | model = UserDomainInfo 28 | fields = ["id", "domain_name", "domain_type", "user_id", "is_enable"] 29 | 30 | 31 | class DomainNameInfoView(BaseModelSet): 32 | authentication_classes = [AdminTokenAuthentication, ] 33 | queryset = UserDomainInfo.objects.all() 34 | serializer_class = AdminDomainNameSerializer 35 | pagination_class = AppsPageNumber 36 | 37 | filter_backends = [DjangoFilterBackend, OrderingFilter] 38 | ordering_fields = ['created_time'] 39 | filterset_class = DomainNameFilter 40 | 41 | def update(self, request, *args, **kwargs): 42 | data = super().update(request, *args, **kwargs).data 43 | instance = self.queryset.filter(pk=data.get('id')).first() 44 | if instance: 45 | reset_app_wx_easy_type(instance.user_id, instance.app_id) 46 | return ApiResponse(**data) 47 | 48 | def destroy(self, request, *args, **kwargs): 49 | instance = self.get_object() 50 | reset_app_wx_easy_type(instance.user_id, instance.app_id) 51 | self.perform_destroy(instance) 52 | return ApiResponse() 53 | -------------------------------------------------------------------------------- /fir_ser/admin/views/report.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 4月 4 | # author: liuyu 5 | # date: 2021/4/11 6 | 7 | import logging 8 | 9 | from django_filters import rest_framework as filters 10 | from django_filters.rest_framework import DjangoFilterBackend 11 | from rest_framework.filters import OrderingFilter 12 | 13 | from admin.utils.serializer import AdminAppReportSerializer 14 | from admin.utils.utils import AppsPageNumber, BaseModelSet 15 | from api.models import AppReportInfo 16 | from common.core.auth import AdminTokenAuthentication 17 | 18 | logger = logging.getLogger(__name__) 19 | 20 | 21 | class ReportFilter(filters.FilterSet): 22 | class Meta: 23 | model = AppReportInfo 24 | fields = ["id", "app_name", "bundle_id", "remote_addr", "report_type", "email", "status", "app_id"] 25 | 26 | 27 | class AdminReportView(BaseModelSet): 28 | authentication_classes = [AdminTokenAuthentication, ] 29 | queryset = AppReportInfo.objects.all() 30 | serializer_class = AdminAppReportSerializer 31 | pagination_class = AppsPageNumber 32 | 33 | filter_backends = [DjangoFilterBackend, OrderingFilter] 34 | ordering_fields = ['created_time'] 35 | filterset_class = ReportFilter 36 | -------------------------------------------------------------------------------- /fir_ser/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/api/__init__.py -------------------------------------------------------------------------------- /fir_ser/api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from .models import * 5 | 6 | admin.site.register(UserInfo) 7 | admin.site.register(Apps) 8 | admin.site.register(AppReleaseInfo) 9 | admin.site.register(Token) 10 | admin.site.register(VerifyName) 11 | admin.site.register(AppStorage) 12 | admin.site.register(Price) 13 | admin.site.register(Order) 14 | admin.site.register(DomainCnameInfo) 15 | admin.site.register(UserDomainInfo) 16 | admin.site.register(UserAdDisplayInfo) 17 | admin.site.register(AppReportInfo) 18 | admin.site.register(RemoteClientInfo) 19 | -------------------------------------------------------------------------------- /fir_ser/api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | name = 'api' 6 | -------------------------------------------------------------------------------- /fir_ser/api/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/api/management/__init__.py -------------------------------------------------------------------------------- /fir_ser/api/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/api/management/commands/__init__.py -------------------------------------------------------------------------------- /fir_ser/api/management/commands/add_user_download_times.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from common.utils.caches import add_user_ds 4 | 5 | 6 | class Command(BaseCommand): 7 | help = 'add user download times' 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument('uid', type=str, default='') 11 | parser.add_argument('download_times', type=int, default=100) 12 | 13 | def handle(self, *args, **options): 14 | add_user_ds(options.get('uid', None), options.get('download_times', 100)) 15 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/create_weixin_menu.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from common.libs.mp.wechat import create_menu 4 | 5 | 6 | class Command(BaseCommand): 7 | help = 'create weixin menu' 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument('menu_json', nargs='?', type=str, default=None) 11 | 12 | def handle(self, *args, **options): 13 | create_menu(options.get('menu_json', None)) 14 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/expire_caches.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from api.tasks import start_api_sever_do_clean 4 | from xsign.utils.iproxy import clean_ip_proxy_infos 5 | 6 | 7 | class Command(BaseCommand): 8 | help = 'Expire caches' 9 | 10 | def handle(self, *args, **options): 11 | start_api_sever_do_clean() 12 | clean_ip_proxy_infos() 13 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/expire_config_caches.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from api.tasks import clean_config_cache 4 | 5 | 6 | class Command(BaseCommand): 7 | help = 'Expire config caches' 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument('key', nargs='?', type=str, default='*') 11 | 12 | def handle(self, *args, **options): 13 | clean_config_cache(options.get('key', '*')) 14 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/restart.py: -------------------------------------------------------------------------------- 1 | from .services.command import BaseActionCommand, Action 2 | 3 | 4 | class Command(BaseActionCommand): 5 | help = 'Restart services' 6 | action = Action.restart.value 7 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/api/management/commands/services/__init__.py -------------------------------------------------------------------------------- /fir_ser/api/management/commands/services/services/__init__.py: -------------------------------------------------------------------------------- 1 | from .beat import * 2 | from .celery_default import * 3 | from .flower import * 4 | from .gunicorn import * 5 | from .uwsgi import * 6 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/services/services/beat.py: -------------------------------------------------------------------------------- 1 | from django.core.cache import cache 2 | 3 | from .base import BaseService 4 | from ..hands import * 5 | 6 | __all__ = ['BeatService'] 7 | 8 | 9 | class BeatService(BaseService): 10 | 11 | def __init__(self, **kwargs): 12 | super().__init__(**kwargs) 13 | self.lock = cache.lock('beat-distribute-start-lock', timeout=60) 14 | 15 | @property 16 | def cmd(self): 17 | scheduler = "django_celery_beat.schedulers:DatabaseScheduler" 18 | print("\n- Start Beat as Periodic Task Scheduler") 19 | cmd = [ 20 | 'celery', '-A', 21 | 'fir_ser', 'beat', 22 | '-l', 'INFO', 23 | '--scheduler', scheduler, 24 | '--max-interval', '60' 25 | ] 26 | if self.uid: 27 | cmd.extend(['--uid', self.uid]) 28 | if self.gid: 29 | cmd.extend(['--gid', self.gid]) 30 | 31 | return cmd 32 | 33 | @property 34 | def cwd(self): 35 | return APPS_DIR 36 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/services/services/celery_base.py: -------------------------------------------------------------------------------- 1 | from .base import BaseService 2 | from ..hands import * 3 | 4 | 5 | class CeleryBaseService(BaseService): 6 | 7 | def __init__(self, queue, celery_num=10, **kwargs): 8 | super().__init__(**kwargs) 9 | self.queue = queue 10 | self.celery_num = celery_num 11 | 12 | @property 13 | def cmd(self): 14 | print('\n- Start Celery as Distributed Task Queue: {}'.format(self.queue.capitalize())) 15 | 16 | os.environ.setdefault('PYTHONOPTIMIZE', '1') 17 | os.environ.setdefault('ANSIBLE_FORCE_COLOR', 'True') 18 | 19 | if os.getuid() == 0: 20 | os.environ.setdefault('C_FORCE_ROOT', '1') 21 | server_hostname = os.environ.get("SERVER_HOSTNAME") 22 | if not server_hostname: 23 | server_hostname = '%n' 24 | 25 | cmd = [ 26 | 'celery', '-A', 27 | 'fir_ser', 'worker', 28 | '-l', 'INFO', 29 | '-c', str(self.celery_num), 30 | '-Q', self.queue, 31 | '-n', f'{self.queue}@{server_hostname}' 32 | ] 33 | if self.uid: 34 | cmd.extend(['--uid', self.uid]) 35 | if self.gid: 36 | cmd.extend(['--gid', self.gid]) 37 | return cmd 38 | 39 | @property 40 | def cwd(self): 41 | return APPS_DIR 42 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/services/services/celery_default.py: -------------------------------------------------------------------------------- 1 | from .celery_base import CeleryBaseService 2 | 3 | __all__ = ['CeleryDefaultService'] 4 | 5 | 6 | class CeleryDefaultService(CeleryBaseService): 7 | 8 | def __init__(self, **kwargs): 9 | kwargs['queue'] = 'celery' 10 | super().__init__(**kwargs) 11 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/services/services/flower.py: -------------------------------------------------------------------------------- 1 | from .base import BaseService 2 | from ..hands import * 3 | 4 | __all__ = ['FlowerService'] 5 | 6 | 7 | class FlowerService(BaseService): 8 | 9 | def __init__(self, **kwargs): 10 | super().__init__(**kwargs) 11 | 12 | @property 13 | def cmd(self): 14 | print("\n- Start Flower as Task Monitor") 15 | 16 | if os.getuid() == 0: 17 | os.environ.setdefault('C_FORCE_ROOT', '1') 18 | cmd = [ 19 | 'celery', '-A', 20 | 'fir_ser', 'flower', 21 | '-l', 'INFO', 22 | '--url_prefix=/flower', 23 | '--auto_refresh=False', 24 | '--max_tasks=1000', 25 | f'--address={CELERY_FLOWER_HOST}', 26 | f'--port={CELERY_FLOWER_PORT}', 27 | # '--basic_auth=flower:ninevenxxx' 28 | # '--tasks_columns=uuid,name,args,state,received,started,runtime,worker' 29 | ] 30 | if self.uid: 31 | cmd.extend(['--uid', self.uid]) 32 | if self.gid: 33 | cmd.extend(['--gid', self.gid]) 34 | return cmd 35 | 36 | @property 37 | def cwd(self): 38 | return APPS_DIR 39 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/services/services/gunicorn.py: -------------------------------------------------------------------------------- 1 | from .base import BaseService 2 | from ..hands import * 3 | 4 | __all__ = ['GunicornService'] 5 | 6 | 7 | class GunicornService(BaseService): 8 | 9 | def __init__(self, **kwargs): 10 | self.worker = kwargs['worker_gunicorn'] 11 | super().__init__(**kwargs) 12 | 13 | @property 14 | def cmd(self): 15 | print("\n- Start Gunicorn WSGI HTTP Server") 16 | 17 | log_format = '%(h)s %(t)s %(L)ss "%(r)s" %(s)s %(b)s ' 18 | bind = f'{HTTP_HOST}:{HTTP_PORT}' 19 | cmd = [ 20 | 'gunicorn', 'fir_ser.wsgi', 21 | '-b', bind, 22 | '-k', 'gthread', 23 | '--threads', '10', 24 | '-w', str(self.worker), 25 | '--max-requests', '4096', 26 | '--access-logformat', log_format, 27 | '--access-logfile', '-' 28 | ] 29 | if DEBUG: 30 | cmd.append('--reload') 31 | return cmd 32 | 33 | @property 34 | def cwd(self): 35 | return APPS_DIR 36 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/services/services/uwsgi.py: -------------------------------------------------------------------------------- 1 | from .base import BaseService 2 | from ..hands import * 3 | 4 | __all__ = ['UwsgiService'] 5 | 6 | 7 | class UwsgiService(BaseService): 8 | 9 | def __init__(self, **kwargs): 10 | self.processes = kwargs['uwsgi_processes'] 11 | self.threads = kwargs['uwsgi_threads'] 12 | self.uwsgi_socket_mode = kwargs['uwsgi_socket_mode'] 13 | super().__init__(**kwargs) 14 | 15 | @property 16 | def cmd(self): 17 | if os.getuid() == 0: 18 | os.environ.setdefault('C_FORCE_ROOT', '1') 19 | print("\n- Start Uwsgi WSGI HTTP Server") 20 | bind = f'{SOCKET_HOST}:{SOCKET_PORT}' 21 | cmd = [ 22 | 'uwsgi', 23 | '--processes', f'{self.processes}', 24 | '--threads', f'{self.threads}', 25 | '--wsgi-file', f"{BASE_DIR}/fir_ser/wsgi.py", 26 | '--listen', '512', 27 | '--chdir', BASE_DIR, 28 | '--buffer-size', '65536', 29 | '--vacuum', 30 | '--enable-threads', 31 | '--master', 32 | ] 33 | if self.uid: 34 | cmd.extend(['--uid', self.uid]) 35 | if self.gid: 36 | cmd.extend(['--gid', self.gid]) 37 | if self.uwsgi_socket_mode: 38 | cmd.extend(['--socket', bind]) 39 | else: 40 | cmd.extend(['--http', bind]) 41 | if DEBUG: 42 | cmd.extend(['--touch-reload', BASE_DIR]) 43 | return cmd 44 | 45 | @property 46 | def cwd(self): 47 | return APPS_DIR 48 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/start.py: -------------------------------------------------------------------------------- 1 | from .services.command import BaseActionCommand, Action 2 | 3 | 4 | class Command(BaseActionCommand): 5 | help = 'Start services' 6 | action = Action.start.value 7 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/status.py: -------------------------------------------------------------------------------- 1 | from .services.command import BaseActionCommand, Action 2 | 3 | 4 | class Command(BaseActionCommand): 5 | help = 'Show services status' 6 | action = Action.status.value 7 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/stop.py: -------------------------------------------------------------------------------- 1 | from .services.command import BaseActionCommand, Action 2 | 3 | 4 | class Command(BaseActionCommand): 5 | help = 'Stop services' 6 | action = Action.stop.value 7 | -------------------------------------------------------------------------------- /fir_ser/api/management/commands/upgrade.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from api.management.commands.services.hands import perform_db_migrate 4 | 5 | 6 | class Command(BaseCommand): 7 | help = 'upgrade database' 8 | 9 | def handle(self, *args, **options): 10 | perform_db_migrate() 11 | -------------------------------------------------------------------------------- /fir_ser/api/migrations/0003_appbundleidblacklist.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.3 on 2022-02-23 22:19 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ('api', '0002_auto_20220215_2129'), 9 | ] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name='AppBundleIdBlackList', 14 | fields=[ 15 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 16 | ('user_uid', models.CharField(default='*', max_length=64, verbose_name='用户ID,*表示全局')), 17 | ('bundle_id', models.CharField(blank=True, max_length=64, verbose_name='bundle id')), 18 | ('enable', models.BooleanField(default=True, verbose_name='是否启用该配置项')), 19 | ('status', models.SmallIntegerField(choices=[(0, '黑名单'), (1, '白名单')], default=1, verbose_name='状态')), 20 | ('description', models.CharField(blank=True, default='', max_length=256, verbose_name='备注')), 21 | ('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), 22 | ], 23 | options={ 24 | 'verbose_name': '新应用上传黑白名单', 25 | 'verbose_name_plural': '新应用上传黑白名单', 26 | 'unique_together': {('user_uid', 'bundle_id')}, 27 | }, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /fir_ser/api/migrations/0007_auto_20220725_1612.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.3 on 2022-07-25 16:12 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api', '0006_auto_20220513_0843'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='domaincnameinfo', 15 | name='is_enable', 16 | field=models.BooleanField(default=False, verbose_name='是否启用该解析'), 17 | ), 18 | migrations.AlterField( 19 | model_name='domaincnameinfo', 20 | name='domain_record', 21 | field=models.CharField(max_length=128, verbose_name='记录值'), 22 | ), 23 | migrations.AddField( 24 | model_name='domaincnameinfo', 25 | name='user_ipk', 26 | field=models.IntegerField(default=0, verbose_name='用户ipk'), 27 | ), 28 | migrations.AlterUniqueTogether( 29 | name='domaincnameinfo', 30 | unique_together={('user_ipk', 'domain_record')}, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /fir_ser/api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/api/migrations/__init__.py -------------------------------------------------------------------------------- /fir_ser/api/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/api/utils/__init__.py -------------------------------------------------------------------------------- /fir_ser/api/utils/auth/geetest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # author: liuyu 4 | # date: 2022/2/7 5 | import hashlib 6 | import logging 7 | 8 | from common.libs.geetest.geetest_utils import first_register, second_validate 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | class GeeTestAuth(object): 14 | def __init__(self, unique_key='', ip_address=''): 15 | self.ip_address = ip_address 16 | self.unique_key = unique_key 17 | 18 | def generate(self): 19 | assert self.unique_key 20 | assert self.ip_address 21 | sha = hashlib.sha1(str(self.unique_key).encode("utf-8")) 22 | return first_register(sha.hexdigest(), self.ip_address) 23 | 24 | def valid(self, geetest_data): 25 | return second_validate(geetest_data).get("result", "") == "success" 26 | -------------------------------------------------------------------------------- /fir_ser/api/utils/auth/mfa.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # author: liuyu 4 | # date: 2022/2/7 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | -------------------------------------------------------------------------------- /fir_ser/api/utils/auth/weixin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # author: liuyu 4 | # date: 2022/2/7 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | -------------------------------------------------------------------------------- /fir_ser/api/utils/response.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 4月 4 | # author: NinEveN 5 | # date: 2021/4/16 6 | 7 | 8 | class BaseResponse(object): 9 | def __init__(self): 10 | self.code = 1000 11 | self.msg = "success" 12 | self.data = None 13 | 14 | @property 15 | def dict(self): 16 | return self.__dict__ 17 | -------------------------------------------------------------------------------- /fir_ser/api/utils/signalutils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 3月 4 | # author: NinEveN 5 | # date: 2022/3/3 6 | from common.core.signals import run_resign_task_signal, delete_app_signal, xsign_app_download_url_signal, \ 7 | xsign_migrate_data_signal, xsign_clean_data_signal, xsign_app_release_obj_signal 8 | 9 | 10 | def run_signal_resign_utils(app_obj): 11 | return run_resign_task_signal.send(sender=None, app_obj=app_obj) 12 | 13 | 14 | def run_delete_app_signal(app_obj): 15 | return delete_app_signal.send(None, app_pk=app_obj) 16 | 17 | 18 | def run_xsign_app_download_url(app_obj, udid, download_url_type, limit): 19 | return xsign_app_download_url_signal.send(None, app_pk=app_obj.get('pk'), udid=udid, 20 | download_url_type=download_url_type, limit=limit)[0][1] 21 | 22 | 23 | def run_xsign_migrate_data(app_release_obj, user_obj, new_storage_obj, clean_old_data): 24 | return xsign_migrate_data_signal.send(None, app_release_obj=app_release_obj, user_obj=user_obj, 25 | new_storage_obj=new_storage_obj, clean_old_data=clean_old_data) 26 | 27 | 28 | def run_xsign_clean_data(app_release_obj, storage_obj): 29 | return xsign_clean_data_signal.send(None, app_release_obj=app_release_obj, storage_obj=storage_obj) 30 | 31 | 32 | def run_get_xsign_binary_file(binary_file): 33 | return xsign_app_release_obj_signal.send(None, binary_file=binary_file)[0][1] 34 | -------------------------------------------------------------------------------- /fir_ser/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 10月 4 | # author: liuyu 5 | # date: 2020/10/12 6 | 7 | from xsign.utils.signals import main 8 | 9 | main() 10 | -------------------------------------------------------------------------------- /fir_ser/api/views/getip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: fir_ser 4 | # filename: getip 5 | # author: liuyu 6 | # date: 2022/3/28 7 | 8 | import logging 9 | 10 | from rest_framework.response import Response 11 | from rest_framework.views import APIView 12 | 13 | from common.base.baseutils import get_real_ip_address 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | class GetRemoteIp(APIView): 19 | 20 | def get(self, request): 21 | logger.info(f"real_ip:{get_real_ip_address(request)}") 22 | return Response({'real_ip': get_real_ip_address(request)}) 23 | -------------------------------------------------------------------------------- /fir_ser/api/views/logout.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.contrib import auth 4 | from rest_framework.response import Response 5 | from rest_framework.views import APIView 6 | 7 | from api.models import Token 8 | from common.cache.storage import RedisCacheBase 9 | from common.core.auth import ExpiringTokenAuthentication 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | class LogoutView(APIView): 15 | authentication_classes = [ExpiringTokenAuthentication] 16 | 17 | def delete(self, request): 18 | logger.info(f"user:{request.user} logout") 19 | user = request.user.pk 20 | auth_token = request.auth 21 | RedisCacheBase(auth_token).del_storage_cache() 22 | Token.objects.filter(user=user, access_token=auth_token).delete() 23 | auth.logout(request) 24 | return Response({"code": 1000}) 25 | -------------------------------------------------------------------------------- /fir_ser/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/cli/__init__.py -------------------------------------------------------------------------------- /fir_ser/cli/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CliConfig(AppConfig): 5 | name = 'cli' 6 | -------------------------------------------------------------------------------- /fir_ser/cli/urls.py: -------------------------------------------------------------------------------- 1 | """fir_ser 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.urls import re_path 17 | 18 | from cli.views.apps import CliAppsView, CliAppInfoView, CliAppReleaseInfoView 19 | from cli.views.login import CliUserInfoView 20 | from cli.views.uploads import CliAppAnalyseView, CliUploadView 21 | 22 | urlpatterns = [ 23 | re_path("^apps$", CliAppsView.as_view()), 24 | re_path(r"^apps/(?P\w+)", CliAppInfoView.as_view()), 25 | re_path(r"^appinfos/(?P\w+)/(?P\w+)", CliAppReleaseInfoView.as_view()), 26 | re_path("^upload$", CliUploadView.as_view()), 27 | re_path("^userinfo", CliUserInfoView.as_view()), 28 | re_path("^analyse$", CliAppAnalyseView.as_view()), 29 | ] 30 | -------------------------------------------------------------------------------- /fir_ser/cli/views/apps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 3月 4 | # author: liuyu 5 | # date: 2020/3/4 6 | 7 | import logging 8 | 9 | from api.views.apps import AppsView, AppInfoView, AppReleaseInfoView 10 | from common.core.auth import ApiTokenAuthentication 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | 15 | class CliAppsView(AppsView): 16 | authentication_classes = [ApiTokenAuthentication, ] 17 | 18 | 19 | class CliAppInfoView(AppInfoView): 20 | authentication_classes = [ApiTokenAuthentication, ] 21 | 22 | 23 | class CliAppReleaseInfoView(AppReleaseInfoView): 24 | authentication_classes = [ApiTokenAuthentication, ] 25 | -------------------------------------------------------------------------------- /fir_ser/cli/views/login.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from api.views.login import UserInfoView 4 | from common.core.auth import ApiTokenAuthentication 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class CliUserInfoView(UserInfoView): 10 | authentication_classes = [ApiTokenAuthentication, ] 11 | -------------------------------------------------------------------------------- /fir_ser/cli/views/uploads.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 3月 4 | # author: liuyu 5 | # date: 2020/3/6 6 | 7 | 8 | from api.views.uploads import AppAnalyseView, UploadView 9 | from common.core.auth import ApiTokenAuthentication 10 | 11 | 12 | class CliAppAnalyseView(AppAnalyseView): 13 | authentication_classes = [ApiTokenAuthentication, ] 14 | 15 | 16 | class CliUploadView(UploadView): 17 | authentication_classes = [ApiTokenAuthentication, ] 18 | -------------------------------------------------------------------------------- /fir_ser/common/cache/state.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 12月 4 | # author: NinEveN 5 | # date: 2021/12/22 6 | import logging 7 | import time 8 | 9 | from django.core.cache import cache 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | class CacheBaseState(object): 15 | 16 | def __init__(self, key, value=time.time(), timeout=3600 * 24): 17 | self.key = f"CacheBaseState_{self.__class__.__name__}_{key}" 18 | self.value = value 19 | self.timeout = timeout 20 | self.active = False 21 | 22 | def get_state(self): 23 | return cache.get(self.key) 24 | 25 | def del_state(self): 26 | return cache.delete(self.key) 27 | 28 | def __enter__(self): 29 | if cache.get(self.key): 30 | return False 31 | else: 32 | cache.set(self.key, self.value, self.timeout) 33 | self.active = True 34 | return True 35 | 36 | def __exit__(self, exc_type, exc_val, exc_tb): 37 | if self.active: 38 | cache.delete(self.key) 39 | logger.info(f"cache base state __exit__ {exc_type}, {exc_val}, {exc_tb}") 40 | 41 | 42 | class MigrateStorageState(CacheBaseState): 43 | ... 44 | 45 | 46 | class CleanSignDataState(CacheBaseState): 47 | ... 48 | 49 | 50 | class CleanAppSignDataState(CacheBaseState): 51 | ... 52 | 53 | 54 | class CleanErrorBundleIdSignDataState(CacheBaseState): 55 | ... 56 | -------------------------------------------------------------------------------- /fir_ser/common/constants.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: fir_ser 4 | # filename: constants 5 | # author: liuyu 6 | # date: 2022/4/5 7 | 8 | class DeviceStatus(object): 9 | """ 10 | 设备状态: 当开发者设备里面存在 PROCESSING 或 INELIGIBLE 状态时,再次注册的设备状态全部都为 PROCESSING 11 | """ 12 | DISABLED = 'DISABLED' 13 | ENABLED = 'ENABLED' 14 | PROCESSING = 'PROCESSING' 15 | INELIGIBLE = 'INELIGIBLE' 16 | 17 | 18 | class AppleDeveloperStatus(object): 19 | """ 20 | status_choices = ((-1, '疑似被封'), (0, '未激活'), (1, '已激活'), (2, '协议待同意'), 21 | (3, '维护中'), (4, '证书过期'), (5, '证书丢失'), (6, '设备状态异常'), (99, '状态异常')) 22 | 23 | """ 24 | BAN = -1 25 | INACTIVATED = 0 26 | ACTIVATED = 1 27 | AGREEMENT_NOT_AGREED = 2 28 | MAINTENANCE = 3 29 | CERTIFICATE_EXPIRED = 4 30 | CERTIFICATE_MISSING = 5 31 | DEVICE_ABNORMAL = 6 32 | ABNORMAL_STATUS = 99 33 | 34 | 35 | class SignStatus(object): 36 | """ 37 | sign_status_choices = ((0, '新设备入库准备'), (1, '设备ID已经注册'), (2, 'bundelid已经注册'), 38 | (3, '描述文件已经下载'), (4, '已经完成签名打包')) 39 | 40 | """ 41 | SIGNATURE_PREPARE = 0 42 | DEVICE_REGISTRATION_COMPLETE = 1 43 | APP_REGISTRATION_COMPLETE = 2 44 | PROFILE_DOWNLOAD_COMPLETE = 3 45 | SIGNATURE_PACKAGE_COMPLETE = 4 46 | 47 | 48 | class DeviceClass(object): 49 | """ 50 | 苹果设备类型分为6类,每类都有100设备数 51 | """ 52 | APPLE_WATCH = 'APPLE_WATCH' 53 | IPAD = 'IPAD' 54 | IPHONE = 'IPHONE' 55 | IPOD = 'IPOD' 56 | APPLE_TV = 'APPLE_TV' 57 | MAC = 'MAC' 58 | -------------------------------------------------------------------------------- /fir_ser/common/core/dbrouter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # author: NinEveN 4 | # date: 2022/2/14 5 | 6 | class DBRouter(object): 7 | def db_for_read(self, model, **hints): 8 | # 9 | # if model._meta.model_name == 'Price': 10 | # return 'db1' 11 | # else: 12 | # return 'default' 13 | return 'slave' 14 | 15 | def db_for_write(self, model, **hints): 16 | return 'default' 17 | -------------------------------------------------------------------------------- /fir_ser/common/core/exception.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 1月 4 | # author: NinEveN 5 | # date: 2022/1/13 6 | import logging 7 | 8 | from rest_framework.exceptions import Throttled 9 | from rest_framework.views import exception_handler 10 | 11 | from common.core.response import ApiResponse 12 | 13 | logger = logging.getLogger(__file__) 14 | 15 | 16 | def common_exception_handler(exc, context): 17 | # context['view'] 是TextView的对象,想拿出这个对象对应的类名 18 | ret = exception_handler(exc, context) # 是Response对象,它内部有个data 19 | logger.error(f'{context["view"].__class__.__name__} ERROR: {exc} ret:{ret}') 20 | 21 | if isinstance(exc, Throttled): 22 | second = f' {exc.wait} 秒之后' 23 | if not exc.wait: 24 | second = '稍后' 25 | ret.data = { 26 | 'code': 999, 27 | 'detail': f'您手速太快啦,请{second}再次访问', 28 | } 29 | 30 | if not ret: # drf内置处理不了,丢给django 的,我们自己来处理 31 | return ApiResponse(msg='error', result=str(exc), code=5000) 32 | else: 33 | msg = ret.data.get('msg') 34 | if not msg: 35 | msg = 'error' 36 | else: 37 | del ret.data['msg'] 38 | return ApiResponse(msg=msg, status=ret.status_code, **ret.data) 39 | -------------------------------------------------------------------------------- /fir_ser/common/core/response.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 1月 4 | # author: NinEveN 5 | # date: 2022/1/6 6 | 7 | from django.http import FileResponse 8 | from rest_framework.response import Response 9 | 10 | from common.base.baseutils import make_random_uuid 11 | 12 | 13 | def file_response(stream, filename, content_type): 14 | return FileResponse(stream, as_attachment=True, 15 | filename=filename, 16 | content_type=content_type) 17 | 18 | 19 | def mobileprovision_file_response(file_path): 20 | return file_response(open(file_path, 'rb'), make_random_uuid() + '.mobileprovision', 21 | "application/x-apple-aspen-config") 22 | 23 | 24 | class ApiResponse(Response): 25 | def __init__(self, code=1000, msg='success', data=None, status=None, headers=None, content_type=None, **kwargs): 26 | dic = { 27 | 'code': code, 28 | 'msg': msg 29 | } 30 | if data is not None: 31 | dic['data'] = data 32 | dic.update(kwargs) 33 | self._data = data 34 | # 对象来调用对象的绑定方法,会自动传值 35 | super().__init__(data=dic, status=status, headers=headers, content_type=content_type) 36 | 37 | # 类来调用对象的绑定方法,这个方法就是一个普通函数,有几个参数就要传几个参数 38 | # Response.__init__(data=dic,status=status,headers=headers,content_type=content_type) 39 | -------------------------------------------------------------------------------- /fir_ser/common/core/signals.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # author: NinEveN 4 | # date: 2022/2/15 5 | import logging 6 | 7 | from django.dispatch import Signal 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | """ 12 | 更新应用的时候,是否同时更新超级签名数据 13 | """ 14 | run_resign_task_signal = Signal() 15 | 16 | """ 17 | 删除应用时的信号,用户清理签名相关数据 18 | """ 19 | 20 | delete_app_signal = Signal() 21 | 22 | """ 23 | 下载超级签名数据 24 | """ 25 | xsign_app_download_url_signal = Signal() 26 | 27 | """ 28 | 迁移超级签名数据 29 | """ 30 | xsign_migrate_data_signal = Signal() 31 | 32 | """ 33 | 清理超级签名数据 34 | """ 35 | xsign_clean_data_signal = Signal() 36 | 37 | """ 38 | 根据binary_file获取签名应用数据 39 | """ 40 | xsign_app_release_obj_signal = Signal() 41 | -------------------------------------------------------------------------------- /fir_ser/common/libs/geetest/geetest_lib_result.py: -------------------------------------------------------------------------------- 1 | # sdk lib包的返回结果信息。 2 | class GeetestLibResult: 3 | 4 | def __init__(self): 5 | self.status = 0 # 成功失败的标识码,1表示成功,0表示失败 6 | self.data = '' # 返回数据,json格式 7 | self.msg = '' # 备注信息,如异常信息等 8 | 9 | def set_all(self, status, data, msg): 10 | self.status = status 11 | self.data = data 12 | self.msg = msg 13 | 14 | def __str__(self): 15 | return "GeetestLibResult{{status={0}, data={1}, msg={2}}}".format(self.status, self.data, self.msg) 16 | -------------------------------------------------------------------------------- /fir_ser/common/libs/mp/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 9月 4 | # author: NinEveN 5 | # date: 2021/9/6 6 | -------------------------------------------------------------------------------- /fir_ser/common/libs/mp/chat/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 9月 4 | # author: NinEveN 5 | # date: 2021/9/6 6 | -------------------------------------------------------------------------------- /fir_ser/common/libs/mp/ierror.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | ######################################################################### 4 | # Author: jonyqin 5 | # Created Time: Thu 11 Sep 2014 01:53:58 PM CST 6 | # File Name: ierror.py 7 | # Description:定义错误码含义 8 | ######################################################################### 9 | WXBizMsgCrypt_OK = 0 10 | WXBizMsgCrypt_ValidateSignature_Error = -40001 11 | WXBizMsgCrypt_ParseXml_Error = -40002 12 | WXBizMsgCrypt_ComputeSignature_Error = -40003 13 | WXBizMsgCrypt_IllegalAesKey = -40004 14 | WXBizMsgCrypt_ValidateAppid_Error = -40005 15 | WXBizMsgCrypt_EncryptAES_Error = -40006 16 | WXBizMsgCrypt_DecryptAES_Error = -40007 17 | WXBizMsgCrypt_IllegalBuffer = -40008 18 | WXBizMsgCrypt_EncodeBase64_Error = -40009 19 | WXBizMsgCrypt_DecodeBase64_Error = -40010 20 | WXBizMsgCrypt_GenReturnXml_Error = -40011 21 | -------------------------------------------------------------------------------- /fir_ser/common/libs/pay/alipay/exceptions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | """ 4 | exceptions.py 5 | ~~~~~~~~~~ 6 | 7 | """ 8 | 9 | 10 | class AliPayException(Exception): 11 | def __init__(self, code, message): 12 | self.__code = code 13 | self.__message = message 14 | 15 | def to_unicode(self): 16 | return "AliPayException: code:{}, message:{}".format(self.__code, self.__message) 17 | 18 | def __str__(self): 19 | return self.to_unicode() 20 | 21 | def __repr__(self): 22 | return self.to_unicode() 23 | 24 | 25 | class AliPayValidationError(Exception): 26 | pass 27 | -------------------------------------------------------------------------------- /fir_ser/common/libs/pay/alipay/loggers.py: -------------------------------------------------------------------------------- 1 | import logging.config 2 | 3 | logging.config.dictConfig({ 4 | "version": 1, 5 | "disable_existing_loggers": False, 6 | "formatters": { 7 | "standard": { 8 | "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s" 9 | }, 10 | }, 11 | "handlers": { 12 | "console": { 13 | "level": "DEBUG", 14 | "formatter": "standard", 15 | "class": "logging.StreamHandler", 16 | }, 17 | }, 18 | "loggers": { 19 | "python-alipay-sdk": { 20 | "handlers": ["console"], 21 | "level": "DEBUG", 22 | } 23 | } 24 | }) 25 | logger = logging.getLogger("python-alipay-sdk") 26 | -------------------------------------------------------------------------------- /fir_ser/common/libs/pay/util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 4月 4 | # author: NinEveN 5 | # date: 2021/4/18 6 | from common.core.sysconfig import Config 7 | from common.libs.pay.ali import Alipay 8 | from common.libs.pay.wx import Weixinpay 9 | 10 | 11 | def get_pay_obj_form_name(pay_name): 12 | for pay_info in Config.PAY_CONFIG_KEY_INFO: 13 | if pay_name == pay_info.get('NAME', '') and pay_info.get('ENABLED', False): 14 | auth_info = pay_info.get('AUTH', None) 15 | p_type = pay_info.get('TYPE', '') 16 | if auth_info: 17 | if p_type == 'ALI': 18 | return Alipay(pay_name, p_type, auth_info) 19 | elif p_type == 'WX': 20 | return Weixinpay(pay_name, p_type, auth_info) 21 | else: 22 | pass 23 | 24 | 25 | def get_enable_pay_choices(): 26 | pay_choices = [] 27 | for pay_info in Config.PAY_CONFIG_KEY_INFO: 28 | if pay_info.get('ENABLED', False): 29 | pay_choices.append({'type': pay_info.get('TYPE'), 'name': pay_info.get('NAME', '')}) 30 | return pay_choices 31 | 32 | 33 | def get_payment_type(p_type): 34 | if p_type == 'ALI': 35 | return 1 36 | elif p_type == 'WX': 37 | return 0 38 | -------------------------------------------------------------------------------- /fir_ser/common/libs/sendmsg/aliyunApi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 3月 4 | # author: liuyu 5 | # date: 2020/3/22 6 | 7 | 8 | import json 9 | 10 | from aliyunsdkcore.client import AcsClient 11 | from aliyunsdkcore.request import CommonRequest 12 | 13 | 14 | class AliMsgSender(object): 15 | 16 | def __init__(self, access_key, secret_key, sing_name, template_code, region_id='cn-hangzhou'): 17 | self.regionid = region_id 18 | self.sing_name = sing_name 19 | self.template_code = template_code 20 | self.client = AcsClient(access_key, secret_key, region_id) 21 | 22 | def send_msg(self, template_code, phone, code): 23 | request = CommonRequest() 24 | request.set_accept_format('json') 25 | request.set_domain('dysmsapi.aliyuncs.com') 26 | request.set_method('POST') 27 | request.set_protocol_type('https') 28 | request.set_version('2017-05-25') 29 | request.set_action_name('SendSms') 30 | request.add_query_param('RegionId', self.regionid) 31 | request.add_query_param('PhoneNumbers', phone) 32 | request.add_query_param('SignName', self.sing_name) 33 | request.add_query_param('TemplateCode', template_code) 34 | request.add_query_param('TemplateParam', {'code': code}) 35 | response = self.client.do_action_with_exception(request) 36 | data = json.loads(response) 37 | if data.get('Code') == 'OK': 38 | return True, data.get('Message') 39 | else: 40 | return False, data.get('Message') 41 | 42 | def send_msg_by_act(self, phone, code, act): 43 | if act not in self.template_code.keys(): 44 | return False, f'act {act} not found' 45 | return self.send_msg(self.template_code.get(act), phone, code) 46 | -------------------------------------------------------------------------------- /fir_ser/common/libs/sendmsg/jiguangApi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 3月 4 | # author: liuyu 5 | # date: 2020/3/22 6 | 7 | import json 8 | 9 | import requests 10 | 11 | 12 | class JiGuangMsgSender(object): 13 | BASE_URL = 'https://api.sms.jpush.cn/v1/' 14 | 15 | def __init__(self, app_key, master_secret, sign_id, template_code): 16 | self.template_code = template_code 17 | self.sign_id = sign_id 18 | self.session = requests.Session() 19 | self.session.auth = (app_key, master_secret) 20 | 21 | def _post(self, end_point, body): 22 | return self._request('POST', end_point, body) 23 | 24 | def _request(self, method, end_point, body=None): 25 | uri = self.BASE_URL + end_point 26 | if body is not None: 27 | body = json.dumps(body) 28 | r = self.session.request(method, uri, data=body) 29 | if 0 == len(r.content): 30 | return r.status_code 31 | else: 32 | return r.json() 33 | 34 | def send_msg(self, template_code, phone, code): 35 | body = { 36 | 'mobile': phone, 37 | 'temp_id': template_code, 38 | 'temp_para': {'code': code}, 39 | 'sign_id': self.sign_id 40 | } 41 | response = self._post('messages', body) 42 | if response.get('msg_id', None): 43 | return True, response.get('msg_id') 44 | else: 45 | return False, response.get('error') 46 | 47 | def send_msg_by_act(self, phone, code, act): 48 | if act not in self.template_code.keys(): 49 | return False, f'act {act} not found' 50 | return self.send_msg(self.template_code.get(act), phone, code) 51 | -------------------------------------------------------------------------------- /fir_ser/common/notify/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: fir_ser 4 | # filename: wx 5 | # author: liuyu 6 | # date: 2022/3/23 7 | import logging 8 | 9 | from api.utils.modelutils import get_notify_email_queryset 10 | from common.utils.sendmsg import get_sender_email_token 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | 15 | def notify_by_email(user_obj, message_type, html): 16 | for notify_email in get_notify_email_queryset(user_obj, message_type): 17 | email = notify_email.get('email') 18 | if email: 19 | get_sender_email_token('email', email, 'msg', html) 20 | -------------------------------------------------------------------------------- /fir_ser/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | function cleanup() 3 | { 4 | local pids=`jobs -p` 5 | if [[ "${pids}" != "" ]]; then 6 | kill ${pids} >/dev/null 2>/dev/null 7 | fi 8 | } 9 | 10 | action="${1-start}" 11 | service="${2-all}" 12 | 13 | trap cleanup EXIT 14 | 15 | rm -f tmp/*.pid 16 | 17 | if [[ "$action" == "bash" || "$action" == "sh" ]];then 18 | bash 19 | else 20 | python manage.py "${action}" "${service}" -u nginx -usm 1 21 | fi 22 | 23 | -------------------------------------------------------------------------------- /fir_ser/files/embedded.mobileprovision: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/files/embedded.mobileprovision -------------------------------------------------------------------------------- /fir_ser/files/head_img.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/files/head_img.jpeg -------------------------------------------------------------------------------- /fir_ser/fir_ser/__init__.py: -------------------------------------------------------------------------------- 1 | from .celery import app as celery_app 2 | 3 | __all__ = ('celery_app',) 4 | -------------------------------------------------------------------------------- /fir_ser/fir_ser/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for fir_ser 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', 'fir_ser.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /fir_ser/fir_ser/celery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 5月 4 | # author: NinEveN 5 | # date: 2021/5/27 6 | 7 | import os 8 | 9 | from celery import Celery 10 | 11 | # set the default Django settings module for the 'celery' program. 12 | 13 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fir_ser.settings') 14 | 15 | app = Celery('fir_ser') 16 | 17 | # Using a string here means the worker doesn't have to serialize 18 | # the configuration object to child processes. 19 | # - namespace='CELERY' means all celery-related configuration keys 20 | # should have a `CELERY_` prefix. 21 | app.config_from_object('django.conf:settings', namespace='CELERY') 22 | 23 | # Load task modules from all registered Django apps. 24 | app.autodiscover_tasks() 25 | 26 | 27 | @app.task(bind=True) 28 | def debug_task(self): 29 | print('Request: {0!r}'.format(self.request)) 30 | 31 | 32 | ''' 33 | pip install django-celery-beat==1.1.0 34 | export PYTHONOPTIMIZE=1 35 | celery -A DevOps beat -l info -S django --logfile=./celery.beat.log 36 | 37 | celery multi start b1 -A DevOps beat -l info -S django 38 | 39 | export PYTHONOPTIMIZE=1 40 | celery -O OPTIMIZATION -A DevOps worker -l debug 41 | 42 | celery multi start w1 -O OPTIMIZATION -A DevOps worker -l info --logfile=./celery.worker.log 43 | 44 | celery -A DevOps flower --port=5566 45 | ''' 46 | -------------------------------------------------------------------------------- /fir_ser/fir_ser/urls.py: -------------------------------------------------------------------------------- 1 | """fir_ser 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 re_path, include 18 | 19 | from admin.views.celery_flower import CeleryFlowerView 20 | 21 | # from django.views.static import serve 22 | # from fir_ser import settings 23 | 24 | urlpatterns = [ 25 | re_path('fly.admin/', admin.site.urls), 26 | # web api 请求地址 27 | re_path("api/v1/fir/server/", include('api.urls')), 28 | # client 脚本请求地址 29 | re_path("api/v2/fir/server/", include('cli.urls')), 30 | # 管理后台请求地址 31 | re_path("api/v3/fir/server/", include('admin.urls')), 32 | # 图片验证码 33 | re_path('^captcha/', include('captcha.urls')), 34 | 35 | # 该配置暂时无用 36 | # media路径配置 如果未开启token 授权,可以启动下面配置,直接让nginx读取资源,无需 uwsgi 进行转发 37 | # re_path('^files/(?P.*)$', serve, {'document_root': settings.MEDIA_ROOT}), 38 | 39 | # 任务监控 40 | re_path(r'flower/(?P.*)', CeleryFlowerView.as_view()), 41 | 42 | # 超级签名 43 | re_path("api/v1/fir/xsign/", include('xsign.urls')), 44 | ] 45 | -------------------------------------------------------------------------------- /fir_ser/fir_ser/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for fir_ser 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', 'fir_ser.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /fir_ser/logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/logs/.gitkeep -------------------------------------------------------------------------------- /fir_ser/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', 'fir_ser.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 | -------------------------------------------------------------------------------- /fir_ser/nginx.conf: -------------------------------------------------------------------------------- 1 | # For more information on configuration, see: 2 | # * Official English Documentation: http://nginx.org/en/docs/ 3 | # * Official Russian Documentation: http://nginx.org/ru/docs/ 4 | 5 | user nginx; 6 | worker_processes auto; 7 | error_log /data/log/nginx/error.log; 8 | pid /run/nginx.pid; 9 | 10 | # Load dynamic modules. See /usr/share/nginx/README.dynamic. 11 | include /usr/share/nginx/modules/*.conf; 12 | 13 | worker_rlimit_nofile 51200; 14 | 15 | events 16 | { 17 | use epoll; 18 | worker_connections 51200; 19 | multi_accept on; 20 | } 21 | 22 | 23 | http { 24 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 25 | '$status $body_bytes_sent "$http_referer" ' 26 | '"$http_user_agent" "$http_x_forwarded_for"'; 27 | 28 | access_log /data/log/nginx/access.log main; 29 | 30 | sendfile on; 31 | tcp_nopush on; 32 | tcp_nodelay on; 33 | keepalive_timeout 65; 34 | types_hash_max_size 2048; 35 | 36 | 37 | client_max_body_size 200m; 38 | keepalive_timeout 300; 39 | 40 | server_names_hash_bucket_size 512; 41 | client_header_buffer_size 32k; 42 | large_client_header_buffers 4 32k; 43 | 44 | 45 | gzip on; 46 | gzip_min_length 1k; 47 | gzip_buffers 4 16k; 48 | gzip_http_version 1.0; 49 | gzip_comp_level 2; 50 | gzip_types text/plain application/x-javascript text/css application/xml; 51 | gzip_vary on; 52 | gzip_proxied expired no-cache no-store private auth; 53 | gzip_disable "MSIE [1-6]\."; 54 | 55 | include /etc/nginx/mime.types; 56 | default_type application/octet-stream; 57 | 58 | include /etc/nginx/conf.d/*.conf; 59 | } 60 | -------------------------------------------------------------------------------- /fir_ser/requirements.txt: -------------------------------------------------------------------------------- 1 | celery==5.2.7 2 | django-celery-results==2.4.0 3 | django-celery-beat==2.3.0 4 | Django==4.0.8 5 | djangorestframework==3.14.0 6 | djangorestframework-xml==2.0.0 7 | django-simple-captcha==0.5.17 8 | mysqlclient==2.1.1 9 | django-redis==5.2.0 10 | dnspython==2.2.1 11 | oss2==2.16.0 12 | aliyun-python-sdk-sts==3.1.0 13 | qiniu==7.9.0 14 | xmltodict==0.13.0 15 | pyOpenSSL==22.1.0 16 | paramiko==2.11.0 17 | PyJWT==2.5.0 18 | python-daemon==2.3.1 19 | psutil==5.9.2 20 | flower==1.2.0 21 | django-proxy==1.2.1 22 | uWSGI==2.0.20 23 | django-cors-headers==3.13.0 24 | django-filter==22.1 -------------------------------------------------------------------------------- /fir_ser/requirements/rpm_requirements.txt: -------------------------------------------------------------------------------- 1 | python39 python39-devel redis mariadb-devel gcc-c++ gcc openssl-devel wget -------------------------------------------------------------------------------- /fir_ser/tmp/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/tmp/.gitkeep -------------------------------------------------------------------------------- /fir_ser/uwsgi.conf: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | # 对外提供 http 服务的端口 3 | #http = 127.0.0.1:8899 4 | 5 | listen = 512 # set max connections to 1024 in uWSGI 6 | 7 | #the local unix socket file than commnuincate to Nginx 用于和 nginx 进行数据交互的端口 8 | socket = 127.0.0.1:8899 9 | 10 | # the base directory (full path) django 程序的主目录 11 | chdir = ./ 12 | 13 | # Django's wsgi file 14 | wsgi-file = fir_ser/wsgi.py 15 | 16 | touch-reload = fir_ser 17 | #uwsgi 内部解析数据包大小为4K ,自己可以增加64k 18 | buffer-size = 65536 19 | # maximum number of worker processes 20 | processes = 8 21 | 22 | #thread numbers startched in each worker process 23 | threads = 4 24 | 25 | #monitor uwsgi status 通过该端口可以监控 uwsgi 的负载情况 26 | stats = 127.0.0.1:9191 27 | 28 | # 如果以nginx用户运行,则需要授权 ruby 权限 chmod ug+s /usr/local/rvm/rubies/ruby-2.7.0/bin/ruby /usr/bin/isign 否则,会导致登录信息保存失败 29 | uid=nginx 30 | gid=nginx 31 | 32 | # clear environment on exit 33 | vacuum = true 34 | 35 | enable-threads = true 36 | 37 | # 后台运行,并输出日志 38 | daemonize = ./flyapp.log 39 | 40 | # 为了让阿里云cdn可以缓存数据 41 | del-header=cache-control -------------------------------------------------------------------------------- /fir_ser/xsign/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/xsign/__init__.py -------------------------------------------------------------------------------- /fir_ser/xsign/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from .models import * 5 | 6 | admin.site.register(AppIOSDeveloperInfo) 7 | admin.site.register(AppleDeveloperToAppUse) 8 | admin.site.register(APPSuperSignUsedInfo) 9 | admin.site.register(APPToDeveloper) 10 | admin.site.register(AppUDID) 11 | admin.site.register(DeveloperAppID) 12 | admin.site.register(DeveloperDevicesID) 13 | admin.site.register(IosDeveloperBill) 14 | admin.site.register(IosDeveloperPublicPoolBill) 15 | admin.site.register(UDIDsyncDeveloper) 16 | -------------------------------------------------------------------------------- /fir_ser/xsign/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | name = 'xsign' 6 | -------------------------------------------------------------------------------- /fir_ser/xsign/migrations/0004_udidsyncdeveloper_device_class.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.8 on 2022-11-12 08:53 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ('xsign', '0003_deviceabnormaludid_deviceblackudid'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='udidsyncdeveloper', 14 | name='device_class', 15 | field=models.CharField( 16 | choices=[('APPLE_WATCH', 'APPLE_WATCH'), ('IPAD', 'IPAD'), ('IPHONE', 'IPHONE'), ('IPOD', 'IPOD'), 17 | ('APPLE_TV', 'APPLE_TV'), ('MAC', 'MAC')], default='IPHONE', max_length=16, 18 | verbose_name='设备类型'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /fir_ser/xsign/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/xsign/migrations/__init__.py -------------------------------------------------------------------------------- /fir_ser/xsign/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/xsign/utils/__init__.py -------------------------------------------------------------------------------- /fir_ser/xsign/utils/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | # project: 5月 4 | # author: liuyu 5 | # date: 2020/5/7 6 | import logging 7 | 8 | from api.utils.utils import delete_local_files 9 | from common.utils.storage import Storage 10 | from xsign.models import APPToDeveloper 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | 15 | def delete_app_to_dev_and_file(developer_obj, app_id): 16 | app_to_developer_obj = APPToDeveloper.objects.filter(developerid=developer_obj, app_id_id=app_id) 17 | if app_to_developer_obj: 18 | binary_file = app_to_developer_obj.first().binary_file + ".ipa" 19 | delete_local_files(binary_file) 20 | storage = Storage(developer_obj.user_id) 21 | storage.delete_file(binary_file) 22 | app_to_developer_obj.delete() 23 | -------------------------------------------------------------------------------- /fir_ser/xsign/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/xsign/views/__init__.py -------------------------------------------------------------------------------- /fir_ser/xsign/views/admin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/xsign/views/admin/__init__.py -------------------------------------------------------------------------------- /fir_ser/zsign-1.1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nineaiyu/flyapps/cbf842b72cd41d10b76dde33b387c4169dd821a2/fir_ser/zsign-1.1.2.tar.gz -------------------------------------------------------------------------------- /mailhtml/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mailhtml", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "build": "build.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "description": "", 13 | "dependencies": { 14 | "mjml": "^4.12.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /mailhtml/src/app_sign_failed.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 应用【{{app_obj.name}}】签名失败通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 应用【{{app_obj.name}}】签名失败了 21 | 22 | 23 | 签名时间:{{ now_time }} 24 | 25 | 26 | 失败原因:开发者 {{developer_obj.issuer_id}} 状态 {{developer_obj.get_status_display}} 27 | 28 | 29 | 开发者备注:{{developer_obj.description}}。请登录后台查看具体失败信息 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /mailhtml/src/app_sign_over_limit.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 应用限额-签名失败通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 应用【{{app_obj.name}}】签名失败了 21 | 22 | 23 | 签名时间:{{ now_time }} 24 | 25 | 26 | 失败原因:该应用已经使用设备数 {{used_num}},已超过您设置该应用的签名限额 {{limit_number}},当前已经无法安装新设备,为了避免业务使用,您可以修改该应用签名限额 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /mailhtml/src/apple_developer_devices_over_limit.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 账户超级签名余额超过阈值通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 您当前账户超级签名可用设备仅剩 {{device_count}} 21 | 22 | 23 | 已超过您设置的阈值 {{user_obj.notify_available_signs}} 24 | 25 | 26 | 为了避免业务使用,望您尽快添加苹果开发者! 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /mailhtml/src/apple_developer_unavailable.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 超级签名设备余额不足-签名失败通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 应用【{{app_obj.name}}】签名失败了 21 | 22 | 23 | 签名时间:{{ now_time }} 24 | 25 | 26 | 失败原因:可用设备量已经不足或超限,请添加新的苹果开发者或修改开发者设备数量 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /mailhtml/src/base/footer.mjml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FLY 应用分发平台 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /mailhtml/src/base/username.mjml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 尊敬的用户{{ 5 | username }},您好: 6 | 7 | 8 | -------------------------------------------------------------------------------- /mailhtml/src/change_userinfo.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 重要信息变更验证码 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 您的验证码:【{{ code }}】 20 | 21 | 22 | 操作时间:{{ now_time }} 23 | 24 | 25 | 备注:您正在尝试变更重要信息,请妥善保管账户信息 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /mailhtml/src/code_notify.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 验证码通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 您的验证码:【{{ code }}】 20 | 21 | 22 | 操作时间:{{ now_time }} 23 | 24 | 25 | 备注:若非本人操作,请忽略 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /mailhtml/src/download_times_not_enough.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 账户下载余额不足通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 您当前账户下载次数仅剩 {% widthratio user_obj.download_times base_download_times 1 %} 21 | 22 | 23 | 您当前账户下载次数不足,应用已经无法下载安装。为了避免业务暂停使用,望您尽快充值 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /mailhtml/src/download_times_over_limit.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 账户下载余额超过阈值通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 您当前账户下载次数仅剩 {% widthratio user_obj.download_times base_download_times 1 %} 21 | 22 | 23 | 已超过您设置的阈值 {% widthratio user_obj.notify_available_downloads base_download_times 1 %} 24 | 25 | 26 | 为了避免业务暂停使用,望您尽快充值! 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /mailhtml/src/index.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 账户下载余额不足通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 您当前账户下载次数仅剩 {{user_obj.download_times}} 21 | 22 | 23 | 为了避免业务使用,望您尽快充值! 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /mailhtml/src/login_code.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 登录验证码 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 您的验证码:【{{ code }}】 20 | 21 | 22 | 操作时间:{{ now_time }} 23 | 24 | 25 | 备注:您正在进行登录操作,若非本人操作,请勿泄露 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /mailhtml/src/pay_success.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 充值到账通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 您已经成功充值,订单信息如下 21 | 22 | 23 | 订单号:{{order_obj.order_number}} 24 | 25 | 26 | 支付时间:{{order_obj.pay_time}} 27 | 28 | 29 | 支付方式:{{order_obj.get_payment_type_display}} 30 | 31 | 32 | 订单信息:您充值了 {% widthratio order_obj.actual_download_times base_download_times 1 %} 下载次数,【赠送 33 | {% widthratio order_obj.actual_download_gift_times base_download_times 1 %}】 34 | 35 | 36 | 备注:{{order_obj.description}} 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /mailhtml/src/register_code.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 注册验证码 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 您的验证码:【{{ code }}】 20 | 21 | 22 | 操作时间:{{ now_time }} 23 | 24 | 25 | 备注:您正在注册成为新用户,感谢您的支持 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /mailhtml/src/reset_password.mjml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 重置密码通知 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 您的新密码:【{{ code }}】 20 | 21 | 22 | 操作时间:{{ now_time }} 23 | 24 | 25 | 备注:您的密码已经重置成功,请用新密码登录,并妥善保管账户信息 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /nginx.conf.d/app.hehelucky.cn.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwG4qbB2yLqgAo 3 | /yejabGADrTv/lbEQtHSxsDDz6u2HFAd63QHLgDTR7v4SOmxd0wwSy3XNFOMZbT5 4 | Okbqtzl8IYgkZfJ/EByuuZsp8KbyRtDTQHB/6SMnVt61QC4esvzNlvhHFRXInC7H 5 | whrdZsuqGo8tEuOrApbOhQ3CTMxYZ7ok3gtzcVOqaCyrRiY9F7tfOe6zHQ5kWJ8D 6 | 1XZuieFNvqh1l42B9BMcrg5rh8rYg9LYWN3HJBR5lFLi8qWFcTq5yp7a7yvozIcK 7 | M5CngzqeHZ5OSm8iWo4ZTuD9lfSdka8y1LucNdtMLMhnr7wAlZ6vjtLvlz80NY+l 8 | So0mX9qRAgMBAAECggEAOMg4kbH8GrAuqwlSM4thswOK7ZLZUSCmXMq+3qP4HpjU 9 | cEE5wRLhKz6G6hBAYXswsqWa2zfA9JTanD1hHk8DlvWM2weP/84nIWMxF33DpnyU 10 | 2nh9jZCTSBznZBOUi0t03ake1nSC+JL7NmdsmBSL+IXlwkfD5qDIFUCrZgmzHZGT 11 | aZNZgS5PL8bf8Prfif/a27zW29cwPV2geX6vnfSouobXc6PX3HGi2++BVNiCY6oW 12 | Z7BJfQ5zRIgyuZTLmYpU5RyEzR/zoIVllcPu07/6Yhp9ptJ/DabOV1Sje3vKpm/i 13 | WWQ0jeZj6yUq1j48xKfcfrV8uE8Zbc6zUGA1F2ZevQKBgQDZhMBY5qVk8mNj7yW3 14 | pdtUKEn2jKaFoItmU9dkn99SilfKGt9ip1QmijuaUs8ZE8z4w4kg4JLnDgFIWvRP 15 | UfSeDy/ah+7gofMDQrlKuhD8eAO2RIKjzHRT1HNCQOxnY9fsh4j1lNYpU75KA1mz 16 | BdkyzRpYnqLAOw73lyfao24BGwKBgQDPQ1GYw1cUbnTNSrWPZkNDDnouWBXInaQt 17 | lrgFFDC4z9ZA1KDKq5nKUPlG6nTia3U/tCy0nCJGD+hKZDxymCmVUcXou9vOlZ7X 18 | TCEpLsWtLlEOcEuCThx0OZp36D+X1dsjuvpi5CsnslJWwlWvANvI+w+ByykHhXz7 19 | 3nUDLWI5wwKBgGQ7JsMQbRBKHQB0t4WbeKib9BEXGLiklPevFKa6gJaxRujLXzo/ 20 | bqhftD1VgTbGCAJtX+f1c9vieTbVKpTOaBDZYKyrTKjYF3neKjHD8YXU66j2gthl 21 | owjp8b6K2iR+PoYwoiy/y7u5agsC5jd3L3GYT1acqtQ3opr2XRgDjPIXAoGBAIXi 22 | x6/156eUHrGKpDQo7CRCEt0jG5dHdgnZBbCKNVr9Lzsb325sYWEW46BSzgSBQ/FN 23 | 018sSNuXZQfBLmccMih59W19BOWzMzOCwx0WUK2pcMTx92UKTkkp+MWiaooLpI61 24 | Nw9wswvmnlfbG3x8An4W6BKBCVUJfw44yr+trnj/AoGAQ69rzKiCZUbvSoQvdMis 25 | 9JsEuT2R5vDJiJI7g6kDbCcaYDxoTx1893DVUrM3hqWt5G4PPUH0aaXZc+mj2ra3 26 | Tu+N95ispKfIWdyVvC55ERvRwE1tHnEWbTEejpWeZlSIpUv98gsc4/gRl4vvLXp7 27 | S+ilLb47MJk/uiYp4ZoUDmo= 28 | -----END PRIVATE KEY----- 29 | 30 | --------------------------------------------------------------------------------