优化代码

super_signature
nineven 5 years ago
parent a4e9579236
commit ff81f62d95
  1. 15
      fir_client/src/App.vue
  2. 9
      fir_client/src/assets/icon/iconfont.css
  3. 53
      fir_client/src/assets/icon/iconfont.svg
  4. 48
      fir_client/src/components/FirAppInfosBase.vue
  5. 62
      fir_client/src/components/FirAppInfosbaseinfo.vue
  6. 6
      fir_client/src/components/FirAppInfoscombo.vue
  7. 9
      fir_client/src/components/FirAppInfosdevices.vue
  8. 175
      fir_client/src/components/FirAppInfossecurity.vue
  9. 19
      fir_client/src/components/FirApps.vue
  10. 186
      fir_client/src/components/FirDownload.vue
  11. 16
      fir_client/src/components/FirHeader.vue
  12. 8
      fir_client/src/components/FirLogin.vue
  13. 415
      fir_client/src/components/FirSuperSignBase.vue
  14. 82
      fir_client/src/components/FirUserProfileBase.vue
  15. 14
      fir_client/src/components/FirUserProfileChangePwd.vue
  16. 43
      fir_client/src/components/FirUserProfileInfo.vue
  17. 92
      fir_client/src/components/FirUserProfileStorage.vue
  18. 1
      fir_client/src/main.js
  19. 85
      fir_client/src/restful/index.js
  20. 2
      fir_client/src/router/index.js
  21. 94
      fir_client/src/utils/index.js
  22. 2
      fir_ser/api/admin.py
  23. 97
      fir_ser/api/migrations/0001_initial.py
  24. 1
      fir_ser/api/migrations/0002_auto_20200503_1120.py
  25. 8
      fir_ser/api/migrations/0003_apptodeveloper.py
  26. 1
      fir_ser/api/migrations/0004_auto_20200504_2054.py
  27. 1
      fir_ser/api/migrations/0005_apptodeveloper_release_file.py
  28. 5
      fir_ser/api/migrations/0006_udidsyncdeveloper.py
  29. 21
      fir_ser/api/urls.py
  30. 20
      fir_ser/api/utils/TokenManager.py
  31. 1
      fir_ser/api/utils/ali/__init__.py
  32. 1
      fir_ser/api/utils/ali/api.py
  33. 1
      fir_ser/api/utils/ali/client/__init__.py
  34. 1
      fir_ser/api/utils/ali/client/api/__init__.py
  35. 1
      fir_ser/api/utils/ali/client/basic.py
  36. 1
      fir_ser/api/utils/ali/pay/__init__.py
  37. 1
      fir_ser/api/utils/ali/pay/api/base.py
  38. 1
      fir_ser/api/utils/ali/yun/api/base.py
  39. 1
      fir_ser/api/utils/ali/yun/api/sms.py
  40. 1
      fir_ser/api/utils/ali/yun/api/vod.py
  41. 2
      fir_ser/api/utils/ali/yun/basic.py
  42. 28
      fir_ser/api/utils/app/apputils.py
  43. 14
      fir_ser/api/utils/app/dadf.py
  44. 26
      fir_ser/api/utils/app/firApi.py
  45. 102
      fir_ser/api/utils/app/iossignapi.py
  46. 13
      fir_ser/api/utils/app/randomstrings.py
  47. 97
      fir_ser/api/utils/app/raqc.py
  48. 80
      fir_ser/api/utils/app/shellcmds.py
  49. 150
      fir_ser/api/utils/app/supersignutils.py
  50. 9
      fir_ser/api/utils/auth.py
  51. 3
      fir_ser/api/utils/captcha_verify.py
  52. 7
      fir_ser/api/utils/crontab/sync_cache.py
  53. 8
      fir_ser/api/utils/middlewares.py
  54. 6
      fir_ser/api/utils/permission.py
  55. 12
      fir_ser/api/utils/response.py
  56. 97
      fir_ser/api/utils/serializer.py
  57. 39
      fir_ser/api/utils/storage/aliyunApi.py
  58. 102
      fir_ser/api/utils/storage/caches.py
  59. 20
      fir_ser/api/utils/storage/localApi.py
  60. 24
      fir_ser/api/utils/storage/qiniuApi.py
  61. 12
      fir_ser/api/utils/storage/storage.py
  62. 17
      fir_ser/api/utils/utils.py
  63. 32
      fir_ser/api/views/apps.py
  64. 95
      fir_ser/api/views/download.py
  65. 55
      fir_ser/api/views/login.py
  66. 2
      fir_ser/api/views/logout.py
  67. 22
      fir_ser/api/views/receiveudids.py
  68. 10
      fir_ser/api/views/storage.py
  69. 112
      fir_ser/api/views/supersign.py
  70. 109
      fir_ser/api/views/uploads.py
  71. 74
      fir_ser/fir_ser/settings.py
  72. 7
      fir_ser/fir_ser/urls.py
  73. 14
      fir_ser/tests/postudid.py

@ -9,8 +9,7 @@
export default {
name: 'app',
data() {
return {
}
return {}
},
}
</script>
@ -20,13 +19,12 @@
background-color: rgba(0, 166, 224, 0.18)
}
@font-face {font-family: "iconfont";
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
@ -74,7 +72,4 @@
}
</style>

@ -1,10 +1,9 @@
@font-face {font-family: "iconfont";
@font-face {
font-family: "iconfont";
src: url('iconfont.eot?t=1583637102320'); /* IE9 */
src: url('iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
src: url('iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('iconfont.woff?t=1583637102320') format('woff'),
url('iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
url('iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {

@ -4,12 +4,12 @@
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font id="iconfont" horiz-adv-x="1024">
<font-face
font-family="iconfont"
font-weight="500"
@ -18,36 +18,53 @@ Created by iconfont
ascent="896"
descent="-128"
/>
<missing-glyph />
<missing-glyph/>
<glyph glyph-name="android" unicode="&#58909;" d="M269.401218 547.905l484.587674 0 0-11.106965c0-120.589472 0.023536-241.183038-0.011256-361.76944-0.011256-40.771589-21.756513-62.327534-62.705134-62.342884-10.184967 0-20.37198 0-29.858028 0 0-43.198871 0.467651-85.178985-0.171915-127.141702-0.397043-26.024723-22.889313-47.623647-48.671513-48.568159-27.686573-1.013073-52.970422 18.175965-53.951773 44.760436-1.579985 42.982954-0.425695 86.064145-0.425695 130.026402l-92.891643 0c0-17.075911 0.033769-34.169217-0.005117-51.256384-0.055259-25.179472 0.483001-50.374294-0.428765-75.523067-0.896416-24.611537-20.249183-44.734854-44.115754-47.749514-25.325805-3.202948-49.012274 11.464099-56.531528 35.426861-1.660826 5.300726-2.638083 11.033287-2.675946 16.571421-0.234337 35.778878-0.174985 71.555709-0.064468 107.33561 0.013303 4.910846 0.836041 9.815553 1.417279 16.121166-12.051477 0-23.86657-0.073678-35.68678 0.011256-34.978652 0.254803-57.743122 22.928199-57.767682 58.021461-0.100284 122.571616-0.039909 245.152443-0.042979 367.729176L269.400195 547.905 269.401218 547.905zM754.197646 583.671599 268.922311 583.671599c4.508687 83.888596 52.172243 136.038326 123.015731 173.068707-7.217379 10.940167-14.230096 21.587668-21.263279 32.217772-5.119601 7.730055-10.30674 15.407921-15.398712 23.150256-3.591805 5.460362-9.344832 11.614525-1.36509 17.062608 8.616238 5.885034 12.135388-2.347464 15.941064-7.959276 9.094122-13.428847 18.064424-26.946722 27.064402-40.440038 11.086499-16.617469 11.078313-16.613376 30.132274-11.05887 60.601216 17.662265 120.804367 16.659425 180.590008-3.89061 2.139733-0.738827 4.331655-1.331321 7.135514-2.183735 12.395308 18.558681 24.680099 36.96182 36.96489 55.358819 1.471514 2.206248 2.910283 4.427846 4.428869 6.598278 3.459798 4.948709 7.856945 7.618515 13.384845 3.53757 5.008061-3.693112 4.154623-8.404414 0.994654-13.125949-10.67513-15.95232-21.285792-31.954783-31.911804-47.940872-2.371-3.571339-4.705162-7.167237-7.42204-11.304464 70.458725-36.700877 118.544907-88.950891 122.990149-173.090197L754.197646 583.671599zM641.381208 678.031687c0.150426 14.033621-11.603269 25.974581-25.713638 26.131147-14.242376 0.159636-25.844621-11.30651-25.957185-25.65224-0.116657-14.520715 11.002588-25.810852 25.501814-25.904996C629.645932 652.518616 641.236921 663.776008 641.381208 678.031687L641.381208 678.031687zM433.369663 678.65488c-0.187265 14.328333-11.883655 25.780153-26.066679 25.512047-14.080693-0.260943-25.824155-12.32777-25.576515-26.275433 0.251733-14.231119 11.936867-25.453718 26.327622-25.300222C422.521594 652.744767 433.556928 664.105512 433.369663 678.65488L433.369663 678.65488zM892.258025 385.416256c-0.004093-36.07359 0.095167-72.149227-0.028653-108.219747-0.11154-31.752168-21.937638-54.680367-51.753711-54.658877-29.79049 0.017396-51.8059 23.100114-51.836599 54.654784-0.067538 73.140811-0.149403 146.276505 0.061398 219.415268 0.075725 26.923186 19.805068 48.538483 45.833885 51.248198 27.095102 2.819209 50.834782-13.987572 56.574506-40.425711 1.105171-5.102205 1.090844-10.510378 1.105171-15.782451 0.089028-35.413558 0.045025-70.821999 0.040932-106.234533L892.258025 385.416256zM234.306932 385.412163c0-37.7293 0.35918-75.465762-0.132006-113.184829-0.302899-23.12672-16.584724-42.608423-38.748513-48.26526-21.668509-5.526877-45.432749 3.454682-56.523341 23.153326-4.52199 8.035-7.7935 17.987676-7.869225 27.101241-0.624217 74.130348-0.546446 148.266835-0.252757 222.399229 0.116657 29.218462 23.174815 51.633984 51.857065 51.633984 28.801976-0.004093 51.323922-22.078855 51.64831-51.639101 0.414439-37.065174 0.102331-74.132394 0.102331-111.199615-0.028653 0-0.055259 0-0.084934 0L234.306932 385.411139zM234.306932 385.412163" horiz-adv-x="1024" />
<glyph glyph-name="android" unicode="&#58909;"
d="M269.401218 547.905l484.587674 0 0-11.106965c0-120.589472 0.023536-241.183038-0.011256-361.76944-0.011256-40.771589-21.756513-62.327534-62.705134-62.342884-10.184967 0-20.37198 0-29.858028 0 0-43.198871 0.467651-85.178985-0.171915-127.141702-0.397043-26.024723-22.889313-47.623647-48.671513-48.568159-27.686573-1.013073-52.970422 18.175965-53.951773 44.760436-1.579985 42.982954-0.425695 86.064145-0.425695 130.026402l-92.891643 0c0-17.075911 0.033769-34.169217-0.005117-51.256384-0.055259-25.179472 0.483001-50.374294-0.428765-75.523067-0.896416-24.611537-20.249183-44.734854-44.115754-47.749514-25.325805-3.202948-49.012274 11.464099-56.531528 35.426861-1.660826 5.300726-2.638083 11.033287-2.675946 16.571421-0.234337 35.778878-0.174985 71.555709-0.064468 107.33561 0.013303 4.910846 0.836041 9.815553 1.417279 16.121166-12.051477 0-23.86657-0.073678-35.68678 0.011256-34.978652 0.254803-57.743122 22.928199-57.767682 58.021461-0.100284 122.571616-0.039909 245.152443-0.042979 367.729176L269.400195 547.905 269.401218 547.905zM754.197646 583.671599 268.922311 583.671599c4.508687 83.888596 52.172243 136.038326 123.015731 173.068707-7.217379 10.940167-14.230096 21.587668-21.263279 32.217772-5.119601 7.730055-10.30674 15.407921-15.398712 23.150256-3.591805 5.460362-9.344832 11.614525-1.36509 17.062608 8.616238 5.885034 12.135388-2.347464 15.941064-7.959276 9.094122-13.428847 18.064424-26.946722 27.064402-40.440038 11.086499-16.617469 11.078313-16.613376 30.132274-11.05887 60.601216 17.662265 120.804367 16.659425 180.590008-3.89061 2.139733-0.738827 4.331655-1.331321 7.135514-2.183735 12.395308 18.558681 24.680099 36.96182 36.96489 55.358819 1.471514 2.206248 2.910283 4.427846 4.428869 6.598278 3.459798 4.948709 7.856945 7.618515 13.384845 3.53757 5.008061-3.693112 4.154623-8.404414 0.994654-13.125949-10.67513-15.95232-21.285792-31.954783-31.911804-47.940872-2.371-3.571339-4.705162-7.167237-7.42204-11.304464 70.458725-36.700877 118.544907-88.950891 122.990149-173.090197L754.197646 583.671599zM641.381208 678.031687c0.150426 14.033621-11.603269 25.974581-25.713638 26.131147-14.242376 0.159636-25.844621-11.30651-25.957185-25.65224-0.116657-14.520715 11.002588-25.810852 25.501814-25.904996C629.645932 652.518616 641.236921 663.776008 641.381208 678.031687L641.381208 678.031687zM433.369663 678.65488c-0.187265 14.328333-11.883655 25.780153-26.066679 25.512047-14.080693-0.260943-25.824155-12.32777-25.576515-26.275433 0.251733-14.231119 11.936867-25.453718 26.327622-25.300222C422.521594 652.744767 433.556928 664.105512 433.369663 678.65488L433.369663 678.65488zM892.258025 385.416256c-0.004093-36.07359 0.095167-72.149227-0.028653-108.219747-0.11154-31.752168-21.937638-54.680367-51.753711-54.658877-29.79049 0.017396-51.8059 23.100114-51.836599 54.654784-0.067538 73.140811-0.149403 146.276505 0.061398 219.415268 0.075725 26.923186 19.805068 48.538483 45.833885 51.248198 27.095102 2.819209 50.834782-13.987572 56.574506-40.425711 1.105171-5.102205 1.090844-10.510378 1.105171-15.782451 0.089028-35.413558 0.045025-70.821999 0.040932-106.234533L892.258025 385.416256zM234.306932 385.412163c0-37.7293 0.35918-75.465762-0.132006-113.184829-0.302899-23.12672-16.584724-42.608423-38.748513-48.26526-21.668509-5.526877-45.432749 3.454682-56.523341 23.153326-4.52199 8.035-7.7935 17.987676-7.869225 27.101241-0.624217 74.130348-0.546446 148.266835-0.252757 222.399229 0.116657 29.218462 23.174815 51.633984 51.857065 51.633984 28.801976-0.004093 51.323922-22.078855 51.64831-51.639101 0.414439-37.065174 0.102331-74.132394 0.102331-111.199615-0.028653 0-0.055259 0-0.084934 0L234.306932 385.411139zM234.306932 385.412163"
horiz-adv-x="1024"/>
<glyph glyph-name="android1" unicode="&#58902;" d="M391.428571 620q9.142857 0 15.714286 6.571429T413.714286 642.285714t-6.571429 15.714286T391.428571 664.571429t-15.428571-6.571429-6.285714-15.714286 6.285714-15.714285 15.428571-6.571429z m241.142858 0q9.142857 0 15.428571 6.571429t6.285714 15.714285-6.285714 15.714286-15.428571 6.571429-15.714286-6.571429T610.285714 642.285714t6.571429-15.714285T632.571429 620zM168.571429 514.857143q24 0 41.142857-17.142857t17.142857-41.142857v-245.714286q0-24.571429-16.857143-41.714286t-41.428571-17.142857-41.714286 17.142857-17.142857 41.714286V456.571429q0 24 17.142857 41.142857t41.714286 17.142857z m605.714285-10.857143v-380.571429q0-26.285714-18.285714-44.571428t-44-18.285714h-42.857143v-129.714286q0-24.571429-17.142857-41.714286t-41.714286-17.142857-41.714285 17.142857-17.142858 41.714286v129.714286H472.571429v-129.714286q0-24.571429-17.142858-41.714286t-41.714285-17.142857q-24 0-41.142857 17.142857t-17.142858 41.714286l-0.571428 129.714286h-42.285714q-26.285714 0-44.571429 18.285714t-18.285714 44.571428V504h524.571428zM641.714286 735.428571q61.142857-31.428571 97.714285-87.714285t36.571429-123.142857H247.428571q0 66.857143 36.571429 123.142857T382.285714 735.428571l-40.571428 74.857143q-4 7.428571 2.857143 11.428572 7.428571 3.428571 11.428571-3.428572l41.142857-75.428571q54.285714 24 114.857143 24t114.857143-24l41.142857 75.428571q4 6.857143 11.428571 3.428572 6.857143-4 2.857143-11.428572z m272.571428-278.857142v-245.714286q0-24.571429-17.142857-41.714286t-41.714286-17.142857q-24 0-41.142857 17.142857t-17.142857 41.714286V456.571429q0 24.571429 17.142857 41.428571t41.142857 16.857143q24.571429 0 41.714286-16.857143t17.142857-41.428571z" horiz-adv-x="1024" />
<glyph glyph-name="android1" unicode="&#58902;"
d="M391.428571 620q9.142857 0 15.714286 6.571429T413.714286 642.285714t-6.571429 15.714286T391.428571 664.571429t-15.428571-6.571429-6.285714-15.714286 6.285714-15.714285 15.428571-6.571429z m241.142858 0q9.142857 0 15.428571 6.571429t6.285714 15.714285-6.285714 15.714286-15.428571 6.571429-15.714286-6.571429T610.285714 642.285714t6.571429-15.714285T632.571429 620zM168.571429 514.857143q24 0 41.142857-17.142857t17.142857-41.142857v-245.714286q0-24.571429-16.857143-41.714286t-41.428571-17.142857-41.714286 17.142857-17.142857 41.714286V456.571429q0 24 17.142857 41.142857t41.714286 17.142857z m605.714285-10.857143v-380.571429q0-26.285714-18.285714-44.571428t-44-18.285714h-42.857143v-129.714286q0-24.571429-17.142857-41.714286t-41.714286-17.142857-41.714285 17.142857-17.142858 41.714286v129.714286H472.571429v-129.714286q0-24.571429-17.142858-41.714286t-41.714285-17.142857q-24 0-41.142857 17.142857t-17.142858 41.714286l-0.571428 129.714286h-42.285714q-26.285714 0-44.571429 18.285714t-18.285714 44.571428V504h524.571428zM641.714286 735.428571q61.142857-31.428571 97.714285-87.714285t36.571429-123.142857H247.428571q0 66.857143 36.571429 123.142857T382.285714 735.428571l-40.571428 74.857143q-4 7.428571 2.857143 11.428572 7.428571 3.428571 11.428571-3.428572l41.142857-75.428571q54.285714 24 114.857143 24t114.857143-24l41.142857 75.428571q4 6.857143 11.428571 3.428572 6.857143-4 2.857143-11.428572z m272.571428-278.857142v-245.714286q0-24.571429-17.142857-41.714286t-41.714286-17.142857q-24 0-41.142857 17.142857t-17.142857 41.714286V456.571429q0 24.571429 17.142857 41.428571t41.142857 16.857143q24.571429 0 41.714286-16.857143t17.142857-41.428571z"
horiz-adv-x="1024"/>
<glyph glyph-name="android2" unicode="&#58922;" d="M256 128c0-25.6 17.066667-42.666667 42.666667-42.666667h42.666666v-149.333333c0-34.133333 29.866667-64 64-64s64 29.866667 64 64V85.333333h85.333334v-149.333333c0-34.133333 29.866667-64 64-64s64 29.866667 64 64V85.333333h42.666666c25.6 0 42.666667 17.066667 42.666667 42.666667V554.666667H256v-426.666667zM149.333333 554.666667C115.2 554.666667 85.333333 524.8 85.333333 490.666667v-298.666667c0-34.133333 29.866667-64 64-64S213.333333 157.866667 213.333333 192v298.666667C213.333333 524.8 183.466667 554.666667 149.333333 554.666667z m725.333334 0c-34.133333 0-64-29.866667-64-64v-298.666667c0-34.133333 29.866667-64 64-64s64 29.866667 64 64v298.666667c0 34.133333-29.866667 64-64 64z m-213.333334 247.466666L716.8 857.6c8.533333 8.533333 8.533333 21.333333 0 29.866667-8.533333 8.533333-21.333333 8.533333-29.866667 0l-64-64C593.066667 844.8 554.666667 853.333333 512 853.333333s-81.066667-8.533333-115.2-25.6L337.066667 891.733333c-8.533333 4.266667-25.6 4.266667-34.133334 0-4.266667-8.533333-4.266667-25.6 0-34.133333l55.466667-55.466667C298.666667 755.2 256 682.666667 256 597.333333h512c0 85.333333-42.666667 162.133333-106.666667 204.8zM426.666667 682.666667H384V725.333333h42.666667v-42.666666z m213.333333 0h-42.666667V725.333333h42.666667v-42.666666z" horiz-adv-x="1024" />
<glyph glyph-name="android2" unicode="&#58922;"
d="M256 128c0-25.6 17.066667-42.666667 42.666667-42.666667h42.666666v-149.333333c0-34.133333 29.866667-64 64-64s64 29.866667 64 64V85.333333h85.333334v-149.333333c0-34.133333 29.866667-64 64-64s64 29.866667 64 64V85.333333h42.666666c25.6 0 42.666667 17.066667 42.666667 42.666667V554.666667H256v-426.666667zM149.333333 554.666667C115.2 554.666667 85.333333 524.8 85.333333 490.666667v-298.666667c0-34.133333 29.866667-64 64-64S213.333333 157.866667 213.333333 192v298.666667C213.333333 524.8 183.466667 554.666667 149.333333 554.666667z m725.333334 0c-34.133333 0-64-29.866667-64-64v-298.666667c0-34.133333 29.866667-64 64-64s64 29.866667 64 64v298.666667c0 34.133333-29.866667 64-64 64z m-213.333334 247.466666L716.8 857.6c8.533333 8.533333 8.533333 21.333333 0 29.866667-8.533333 8.533333-21.333333 8.533333-29.866667 0l-64-64C593.066667 844.8 554.666667 853.333333 512 853.333333s-81.066667-8.533333-115.2-25.6L337.066667 891.733333c-8.533333 4.266667-25.6 4.266667-34.133334 0-4.266667-8.533333-4.266667-25.6 0-34.133333l55.466667-55.466667C298.666667 755.2 256 682.666667 256 597.333333h512c0 85.333333-42.666667 162.133333-106.666667 204.8zM426.666667 682.666667H384V725.333333h42.666667v-42.666666z m213.333333 0h-42.666667V725.333333h42.666667v-42.666666z"
horiz-adv-x="1024"/>
<glyph glyph-name="ios" unicode="&#58892;" d="M521.996 659.402c13.596 0.316 26.587 2.344 38.944 5.874l1.477-1.023c25.485 8.525 47.394 21.707 65.751 38.675l-25.33-22.506c28.822 15.94 52.712 37.447 70.922 63.249l-36.429-39.535c28.139 24 50.335 54.116 64.645 88.349l-34.464-58.168c22.406 27.513 39.057 60.636 47.571 96.894l-25.474-60.785c14.141 25.287 23.872 54.935 27.305 86.475l-11.137-47.132c6.314 18.235 10.235 39.273 10.832 61.141-0.92-14.951-0.492-7.646-0.492-0.24 0 7.406-0.428 14.711-1.261 21.894-114.436-13.762-202.632-110.063-202.632-226.963 0-1.99 0.026-3.974 0.076-5.952zM813.027 279.983c-9.291 23.371-14.678 50.452-14.678 78.791 0 2.616 0.046 5.222 0.137 7.816 0.877 22.699 5.309 44.464 12.755 64.776 8.765 26.429 27.096 55.391 50.856 78.871 1.828 9.48 42.301 41.542 89.752 59.323-84.33 32.038-144.712 69.372-214.043 76.759 0.404 0.843-15.633 2.529-32.046 2.637l-53.878-0.333-15.629-4.312-11.857-3.773-15.091-5.389-10.779-8.623-17.785-7.006-14.552-5.928-10.24-3.773-12.396-3.773h-7.006c-2.507-0.304-5.41-0.478-8.354-0.478s-5.847 0.174-8.699 0.511c-2.079-0.302-4.891-0.455-7.739-0.455-2.848 0-5.659 0.153-8.427 0.452l-5.585-0.030-11.857 3.234-8.623 3.234-12.396 3.234-20.48 8.084-8.084 2.695-14.552 5.389-9.701 3.234-13.474 4.312h-10.24l-14.013 2.695h-10.779l-12.935 3.773c-35.215-0.199-68.68-7.463-99.123-20.442-100.455-45.409-170.936-147.27-170.936-265.593 0-6.498 0.213-12.947 0.631-19.339l-0.046-53.025c0-4.312 0-19.402 3.773-29.103s0-10.779 3.234-16.168 3.234-17.246 5.389-25.869 3.234-11.318 4.851-16.707 4.312-16.168 7.006-24.253 4.312-11.857 6.467-17.785l8.084-22.636 7.545-17.246c3.234-7.006 5.928-14.552 9.162-21.558l8.084-16.168 10.779-21.019 9.162-15.629 11.318-19.402 21.558-32.337 10.24-13.474 12.396-15.629 10.779-12.396 12.396-13.474 11.318-10.779 12.396-11.318 11.318-8.623 12.396-9.162 11.318-6.467 11.857-6.467 10.779-3.773 11.857-3.773 14.013-4.312h12.396c17.291 0.666 33.606 3.944 48.855 9.45 35.686 19.327 84.485 32.203 136.187 33.26-11.243-0.381-6.541-0.215-1.8-0.215 4.741 0 9.443-0.166 14.101-0.492l7.996 0.035 14.552-3.234 9.162-2.695 11.318-3.773 9.701-3.773 14.013-5.928 12.396-5.389 8.084-2.695 10.779-3.773h45.811c39.742 3.516 74.688 21.631 99.914 48.874 71.609 68.094 126.848 162.124 151.674 267.874-5.067-48.749 17.64 18.78 26.966 90.511-106.339-115.262-156.75-58.206-177.238 11.642z" horiz-adv-x="1024" />
<glyph glyph-name="ios" unicode="&#58892;"
d="M521.996 659.402c13.596 0.316 26.587 2.344 38.944 5.874l1.477-1.023c25.485 8.525 47.394 21.707 65.751 38.675l-25.33-22.506c28.822 15.94 52.712 37.447 70.922 63.249l-36.429-39.535c28.139 24 50.335 54.116 64.645 88.349l-34.464-58.168c22.406 27.513 39.057 60.636 47.571 96.894l-25.474-60.785c14.141 25.287 23.872 54.935 27.305 86.475l-11.137-47.132c6.314 18.235 10.235 39.273 10.832 61.141-0.92-14.951-0.492-7.646-0.492-0.24 0 7.406-0.428 14.711-1.261 21.894-114.436-13.762-202.632-110.063-202.632-226.963 0-1.99 0.026-3.974 0.076-5.952zM813.027 279.983c-9.291 23.371-14.678 50.452-14.678 78.791 0 2.616 0.046 5.222 0.137 7.816 0.877 22.699 5.309 44.464 12.755 64.776 8.765 26.429 27.096 55.391 50.856 78.871 1.828 9.48 42.301 41.542 89.752 59.323-84.33 32.038-144.712 69.372-214.043 76.759 0.404 0.843-15.633 2.529-32.046 2.637l-53.878-0.333-15.629-4.312-11.857-3.773-15.091-5.389-10.779-8.623-17.785-7.006-14.552-5.928-10.24-3.773-12.396-3.773h-7.006c-2.507-0.304-5.41-0.478-8.354-0.478s-5.847 0.174-8.699 0.511c-2.079-0.302-4.891-0.455-7.739-0.455-2.848 0-5.659 0.153-8.427 0.452l-5.585-0.030-11.857 3.234-8.623 3.234-12.396 3.234-20.48 8.084-8.084 2.695-14.552 5.389-9.701 3.234-13.474 4.312h-10.24l-14.013 2.695h-10.779l-12.935 3.773c-35.215-0.199-68.68-7.463-99.123-20.442-100.455-45.409-170.936-147.27-170.936-265.593 0-6.498 0.213-12.947 0.631-19.339l-0.046-53.025c0-4.312 0-19.402 3.773-29.103s0-10.779 3.234-16.168 3.234-17.246 5.389-25.869 3.234-11.318 4.851-16.707 4.312-16.168 7.006-24.253 4.312-11.857 6.467-17.785l8.084-22.636 7.545-17.246c3.234-7.006 5.928-14.552 9.162-21.558l8.084-16.168 10.779-21.019 9.162-15.629 11.318-19.402 21.558-32.337 10.24-13.474 12.396-15.629 10.779-12.396 12.396-13.474 11.318-10.779 12.396-11.318 11.318-8.623 12.396-9.162 11.318-6.467 11.857-6.467 10.779-3.773 11.857-3.773 14.013-4.312h12.396c17.291 0.666 33.606 3.944 48.855 9.45 35.686 19.327 84.485 32.203 136.187 33.26-11.243-0.381-6.541-0.215-1.8-0.215 4.741 0 9.443-0.166 14.101-0.492l7.996 0.035 14.552-3.234 9.162-2.695 11.318-3.773 9.701-3.773 14.013-5.928 12.396-5.389 8.084-2.695 10.779-3.773h45.811c39.742 3.516 74.688 21.631 99.914 48.874 71.609 68.094 126.848 162.124 151.674 267.874-5.067-48.749 17.64 18.78 26.966 90.511-106.339-115.262-156.75-58.206-177.238 11.642z"
horiz-adv-x="1024"/>
<glyph glyph-name="IOS" unicode="&#59020;" d="M737.007 358.636c-1.092 104.007 84.833 153.888 88.595 156.316-48.181 70.512-123.305 80.221-150.126 81.435-63.958 6.432-124.761-37.622-157.165-37.622-32.404 0-82.405 36.652-135.441 35.681-69.663-1.092-133.863-40.535-169.787-102.915-72.453-125.732-18.569-311.903 52.065-413.727 34.467-49.881 75.609-105.828 129.616-103.887 51.943 2.063 71.604 33.618 134.47 33.618s80.464-33.618 135.563-32.646c55.948 1.092 91.387 50.851 125.611 100.852 39.564 57.89 55.948 113.959 56.798 116.63-1.214 0.729-109.105 41.991-110.197 166.267zM633.605 663.742c28.641 34.71 47.938 83.012 42.72 131.072-41.264-1.699-91.265-27.428-120.878-62.138-26.457-30.826-49.881-79.978-43.569-127.067 45.997-3.641 93.085 23.423 121.727 58.132z" horiz-adv-x="1024" />
<glyph glyph-name="IOS" unicode="&#59020;"
d="M737.007 358.636c-1.092 104.007 84.833 153.888 88.595 156.316-48.181 70.512-123.305 80.221-150.126 81.435-63.958 6.432-124.761-37.622-157.165-37.622-32.404 0-82.405 36.652-135.441 35.681-69.663-1.092-133.863-40.535-169.787-102.915-72.453-125.732-18.569-311.903 52.065-413.727 34.467-49.881 75.609-105.828 129.616-103.887 51.943 2.063 71.604 33.618 134.47 33.618s80.464-33.618 135.563-32.646c55.948 1.092 91.387 50.851 125.611 100.852 39.564 57.89 55.948 113.959 56.798 116.63-1.214 0.729-109.105 41.991-110.197 166.267zM633.605 663.742c28.641 34.71 47.938 83.012 42.72 131.072-41.264-1.699-91.265-27.428-120.878-62.138-26.457-30.826-49.881-79.978-43.569-127.067 45.997-3.641 93.085 23.423 121.727 58.132z"
horiz-adv-x="1024"/>
<glyph glyph-name="ios1" unicode="&#58881;" d="M615.913 712.238c31.803 40.968 55.898 98.81 47.218 157.832-52.08-3.61-112.838-36.802-148.25-79.991-32.36-39.164-58.954-97.353-48.607-153.876 56.94-1.805 115.545 32.15 149.639 76.035zM896.859 156.8c-22.776-50.481-33.677-73.117-62.98-117.835-40.97-62.496-98.603-140.266-170.055-140.822-63.467-0.694-79.854 41.386-165.957 40.9s-104.156-41.733-167.693-41.107c-71.451 0.695-126.03 70.827-166.929 133.32-114.364 174.568-126.376 379.48-55.897 488.498 50.203 77.354 129.294 122.558 203.661 122.558 75.688 0 123.253-41.593 185.956-41.593 60.758 0 97.768 41.663 185.26 41.663 66.245 0 136.447-36.109 186.441-98.603-163.805-89.922-137.278-324.275 28.192-387.047v0.068h0.001z" horiz-adv-x="1024" />
<glyph glyph-name="ios1" unicode="&#58881;"
d="M615.913 712.238c31.803 40.968 55.898 98.81 47.218 157.832-52.08-3.61-112.838-36.802-148.25-79.991-32.36-39.164-58.954-97.353-48.607-153.876 56.94-1.805 115.545 32.15 149.639 76.035zM896.859 156.8c-22.776-50.481-33.677-73.117-62.98-117.835-40.97-62.496-98.603-140.266-170.055-140.822-63.467-0.694-79.854 41.386-165.957 40.9s-104.156-41.733-167.693-41.107c-71.451 0.695-126.03 70.827-166.929 133.32-114.364 174.568-126.376 379.48-55.897 488.498 50.203 77.354 129.294 122.558 203.661 122.558 75.688 0 123.253-41.593 185.956-41.593 60.758 0 97.768 41.663 185.26 41.663 66.245 0 136.447-36.109 186.441-98.603-163.805-89.922-137.278-324.275 28.192-387.047v0.068h0.001z"
horiz-adv-x="1024"/>
<glyph glyph-name="ios2" unicode="&#58955;" d="M812.2 345.2c-7.2 96.1 56.4 156.8 116.4 199-56.4 50.6-105.4 99.5-209.1 104.5-83.6 3.4-121.8-43.8-200-45.5-72.7-1.7-67.3 52.3-218.2 37.1C170.5 626.8 55.9 505.4 65 335.1c9.1-183.8 147.3-438.3 276.3-438.3 90.9 1.7 112.7 42.1 187.3 42.1 92.7 0 114.5-50.6 187.3-40.5C841.4-84.7 959.5 99 959.5 158c-69.1 33.8-138.2 91.1-147.3 187.2z m-105.4 526C534.1 849.3 485 702.6 494.1 635.2c132.7-6.7 232.7 123.1 212.7 236z" horiz-adv-x="1024" />
<glyph glyph-name="ios2" unicode="&#58955;"
d="M812.2 345.2c-7.2 96.1 56.4 156.8 116.4 199-56.4 50.6-105.4 99.5-209.1 104.5-83.6 3.4-121.8-43.8-200-45.5-72.7-1.7-67.3 52.3-218.2 37.1C170.5 626.8 55.9 505.4 65 335.1c9.1-183.8 147.3-438.3 276.3-438.3 90.9 1.7 112.7 42.1 187.3 42.1 92.7 0 114.5-50.6 187.3-40.5C841.4-84.7 959.5 99 959.5 158c-69.1 33.8-138.2 91.1-147.3 187.2z m-105.4 526C534.1 849.3 485 702.6 494.1 635.2c132.7-6.7 232.7 123.1 212.7 236z"
horiz-adv-x="1024"/>
<glyph glyph-name="Android" unicode="&#59092;" d="M512 192.7c-41.8 0-75.6 34-75.6 75.6V499.7c0 41.6 33.8 75.6 75.6 75.6 20.2 0 39.1-7.8 53.5-22.1 14.3-14.3 22.1-33.3 22.1-53.5v-231.5c0-20.2-7.8-39.1-22.1-53.5-14.4-14-33.3-22-53.5-22M243.1 400c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.4 30.6 0 55.2 24.8 55.2 55.4V344.9c0 30.3-24.8 55.1-55.2 55.1m593-11.4H297.3V409c0 83.4 45.5 160.9 119.9 206.9l-28.9 52.5c-3.6 6.1-4.4 14.1-2.4 21.2 2.2 7.1 7.1 13.4 13.6 16.8 4.1 2.4 8.8 3.6 13.4 3.6 10.2 0 19.7-5.6 24.6-14.6l30.4-55.2c31.4 11.4 64.4 17.3 98.7 17.3 34.8 0 68.1-5.8 99.4-17.5l30.1 55.4c4.9 9 14.3 14.6 24.6 14.6 4.6 0 9.2-1.2 13.4-3.4 6.8-3.6 11.7-9.7 13.6-17 2.2-7.3 1.2-14.8-2.4-21.4l-28.9-52.8c74.4-46 119.9-123.5 119.9-206.9v-19.9h-0.2zM633.8 442.7l38.7 70.5c1.9 3.6 0.7 8.5-2.9 10.5-3.9 1.9-8.5 0.7-10.5-3.2l-38.9-71.2c-32.8 14.6-69.5 22.9-108.2 22.6-38.7 0-75.4-8-107.9-22.6l-38.9 71c-1.9 3.6-6.8 5.1-10.5 3.2s-5.1-6.8-3.2-10.5l38.7-70.5C314.3 403.4 263 329 263 243.4h498c-0.3 85.8-51.3 160.2-127.2 199.3M398.5 333.8c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9s-9.3-20.9-20.9-20.9m226.8 0c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9s-9.5-20.9-20.9-20.9M419.3 66c-41.6 0-75.6 33.8-75.6 75.6V243.7h-19.9c-21.2 0-41.1 8.3-56.2 23.1-15.1 15.1-23.3 34.8-23.1 56.2V702h535.1v-379c0-43.8-35.5-79.3-79.3-79.3h-19.9v-102.1c0-41.6-33.8-75.6-75.6-75.6-20.2 0-39.1 7.8-53.5 22.1-14.3 14.3-22.1 33.3-22.1 53.5V243.7h-34v-102.1c-0.3-41.6-34.4-75.6-75.9-75.6M189 307.7c0-32.6 26.3-59.1 59.1-59.1h40.1V126c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.4V248.8h74.6v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V248.8H624c32.6 0 59.1 26.5 59.1 59.1V666.8H188.8l0.2-359.1z m570.6 6.8c-41.6 0-75.6 33.8-75.6 75.6V621.5c0 41.8 33.8 75.6 75.6 75.6s75.6-33.8 75.6-75.6V390c0-41.7-33.8-75.5-75.6-75.5M512 554.9c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V499.7c0 30.4-24.6 55.2-55.2 55.2M188.2 542.2c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V487c-0.1 30.4-24.9 55.2-55.2 55.2m445.6 207.9l38.7 70.5c1.9 3.6 0.7 8.3-2.9 10.5-3.6 1.9-8.5 0.7-10.5-3.2l-38.9-71.2c-32.8 14.6-69.5 22.9-108.4 22.9-38.7 0-75.4-8-107.9-22.6L365 828c-1.9 3.6-6.8 5.1-10.5 3.2-3.6-1.9-5.1-6.8-3.2-10.5l38.7-70.5c-75.9-39.1-127.2-113.5-127.2-199.1h497.9c0 85.4-51 159.8-126.9 199m-235.3-109c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9 0-11.4-9.3-20.9-20.9-20.9m226.8 0c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9 0-11.4-9.5-20.9-20.9-20.9M264.7 531.7v-358.9c0-32.6 26.3-59.1 59.1-59.1h40.1v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.4V114H549v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V114h40.4c32.6 0 59.1 26.3 59.1 59.1V532l-494.2-0.3zM891 487c0 30.6-24.8 55.2-55.2 55.2-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V487z" horiz-adv-x="1024" />
<glyph glyph-name="android3" unicode="&#58917;" d="M633.472 667.434667h-40.533333v40.533333h40.533333m-202.496-40.533333h-40.490667v40.533333h40.533334m223.914666 74.453333l53.034667 53.077334a20.778667 20.778667 0 0 1 0 28.757333 20.778667 20.778667 0 0 1-28.757333 0L619.306667 804.266667a244.650667 244.650667 0 0 1-107.349334 25.130666c-38.826667 0-75.306667-9.344-107.690666-25.514666L343.893333 864.256a20.309333 20.309333 0 0 1-28.373333 0 20.053333 20.053333 0 0 1 0-28.757333L368.64 782.421333C308.266667 737.877333 269.013333 667.434667 269.013333 586.453333h485.973334c0 80.981333-40.533333 151.850667-100.053334 195.968m201.301334-236.501333a60.757333 60.757333 0 0 1-60.757334-60.714667v-283.477333a60.757333 60.757333 0 0 1 60.757334-60.757333 60.757333 60.757333 0 0 1 60.714666 60.757333V485.205333a60.757333 60.757333 0 0 1-60.714666 60.714667m-688.469334 0a60.757333 60.757333 0 0 1-60.757333-60.714667v-283.477333a60.757333 60.757333 0 0 1 60.757333-60.757333 60.757333 60.757333 0 0 1 60.714667 60.757333V485.205333a60.757333 60.757333 0 0 1-60.714667 60.714667m101.205334-404.949333a40.533333 40.533333 0 0 1 40.533333-40.533334h40.490667v-141.696a60.757333 60.757333 0 0 1 60.714666-60.757333 60.757333 60.757333 0 0 1 60.757334 60.757333v141.738667h80.981333v-141.738667a60.757333 60.757333 0 0 1 60.757333-60.757333 60.757333 60.757333 0 0 1 60.757334 60.757333v141.738667h40.490666a40.533333 40.533333 0 0 1 40.533334 40.490667V545.92H268.970667z" horiz-adv-x="1024" />
<glyph glyph-name="Android" unicode="&#59092;"
d="M512 192.7c-41.8 0-75.6 34-75.6 75.6V499.7c0 41.6 33.8 75.6 75.6 75.6 20.2 0 39.1-7.8 53.5-22.1 14.3-14.3 22.1-33.3 22.1-53.5v-231.5c0-20.2-7.8-39.1-22.1-53.5-14.4-14-33.3-22-53.5-22M243.1 400c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.4 30.6 0 55.2 24.8 55.2 55.4V344.9c0 30.3-24.8 55.1-55.2 55.1m593-11.4H297.3V409c0 83.4 45.5 160.9 119.9 206.9l-28.9 52.5c-3.6 6.1-4.4 14.1-2.4 21.2 2.2 7.1 7.1 13.4 13.6 16.8 4.1 2.4 8.8 3.6 13.4 3.6 10.2 0 19.7-5.6 24.6-14.6l30.4-55.2c31.4 11.4 64.4 17.3 98.7 17.3 34.8 0 68.1-5.8 99.4-17.5l30.1 55.4c4.9 9 14.3 14.6 24.6 14.6 4.6 0 9.2-1.2 13.4-3.4 6.8-3.6 11.7-9.7 13.6-17 2.2-7.3 1.2-14.8-2.4-21.4l-28.9-52.8c74.4-46 119.9-123.5 119.9-206.9v-19.9h-0.2zM633.8 442.7l38.7 70.5c1.9 3.6 0.7 8.5-2.9 10.5-3.9 1.9-8.5 0.7-10.5-3.2l-38.9-71.2c-32.8 14.6-69.5 22.9-108.2 22.6-38.7 0-75.4-8-107.9-22.6l-38.9 71c-1.9 3.6-6.8 5.1-10.5 3.2s-5.1-6.8-3.2-10.5l38.7-70.5C314.3 403.4 263 329 263 243.4h498c-0.3 85.8-51.3 160.2-127.2 199.3M398.5 333.8c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9s-9.3-20.9-20.9-20.9m226.8 0c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9s-9.5-20.9-20.9-20.9M419.3 66c-41.6 0-75.6 33.8-75.6 75.6V243.7h-19.9c-21.2 0-41.1 8.3-56.2 23.1-15.1 15.1-23.3 34.8-23.1 56.2V702h535.1v-379c0-43.8-35.5-79.3-79.3-79.3h-19.9v-102.1c0-41.6-33.8-75.6-75.6-75.6-20.2 0-39.1 7.8-53.5 22.1-14.3 14.3-22.1 33.3-22.1 53.5V243.7h-34v-102.1c-0.3-41.6-34.4-75.6-75.9-75.6M189 307.7c0-32.6 26.3-59.1 59.1-59.1h40.1V126c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.4V248.8h74.6v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V248.8H624c32.6 0 59.1 26.5 59.1 59.1V666.8H188.8l0.2-359.1z m570.6 6.8c-41.6 0-75.6 33.8-75.6 75.6V621.5c0 41.8 33.8 75.6 75.6 75.6s75.6-33.8 75.6-75.6V390c0-41.7-33.8-75.5-75.6-75.5M512 554.9c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V499.7c0 30.4-24.6 55.2-55.2 55.2M188.2 542.2c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V487c-0.1 30.4-24.9 55.2-55.2 55.2m445.6 207.9l38.7 70.5c1.9 3.6 0.7 8.3-2.9 10.5-3.6 1.9-8.5 0.7-10.5-3.2l-38.9-71.2c-32.8 14.6-69.5 22.9-108.4 22.9-38.7 0-75.4-8-107.9-22.6L365 828c-1.9 3.6-6.8 5.1-10.5 3.2-3.6-1.9-5.1-6.8-3.2-10.5l38.7-70.5c-75.9-39.1-127.2-113.5-127.2-199.1h497.9c0 85.4-51 159.8-126.9 199m-235.3-109c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9 0-11.4-9.3-20.9-20.9-20.9m226.8 0c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9 0-11.4-9.5-20.9-20.9-20.9M264.7 531.7v-358.9c0-32.6 26.3-59.1 59.1-59.1h40.1v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.4V114H549v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V114h40.4c32.6 0 59.1 26.3 59.1 59.1V532l-494.2-0.3zM891 487c0 30.6-24.8 55.2-55.2 55.2-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V487z"
horiz-adv-x="1024"/>
<glyph glyph-name="android3" unicode="&#58917;"
d="M633.472 667.434667h-40.533333v40.533333h40.533333m-202.496-40.533333h-40.490667v40.533333h40.533334m223.914666 74.453333l53.034667 53.077334a20.778667 20.778667 0 0 1 0 28.757333 20.778667 20.778667 0 0 1-28.757333 0L619.306667 804.266667a244.650667 244.650667 0 0 1-107.349334 25.130666c-38.826667 0-75.306667-9.344-107.690666-25.514666L343.893333 864.256a20.309333 20.309333 0 0 1-28.373333 0 20.053333 20.053333 0 0 1 0-28.757333L368.64 782.421333C308.266667 737.877333 269.013333 667.434667 269.013333 586.453333h485.973334c0 80.981333-40.533333 151.850667-100.053334 195.968m201.301334-236.501333a60.757333 60.757333 0 0 1-60.757334-60.714667v-283.477333a60.757333 60.757333 0 0 1 60.757334-60.757333 60.757333 60.757333 0 0 1 60.714666 60.757333V485.205333a60.757333 60.757333 0 0 1-60.714666 60.714667m-688.469334 0a60.757333 60.757333 0 0 1-60.757333-60.714667v-283.477333a60.757333 60.757333 0 0 1 60.757333-60.757333 60.757333 60.757333 0 0 1 60.714667 60.757333V485.205333a60.757333 60.757333 0 0 1-60.714667 60.714667m101.205334-404.949333a40.533333 40.533333 0 0 1 40.533333-40.533334h40.490667v-141.696a60.757333 60.757333 0 0 1 60.714666-60.757333 60.757333 60.757333 0 0 1 60.757334 60.757333v141.738667h80.981333v-141.738667a60.757333 60.757333 0 0 1 60.757333-60.757333 60.757333 60.757333 0 0 1 60.757334 60.757333v141.738667h40.490666a40.533333 40.533333 0 0 1 40.533334 40.490667V545.92H268.970667z"
horiz-adv-x="1024"/>
</font>
</defs></svg>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

@ -2,7 +2,7 @@
<el-main>
<div class="page-app app-info">
<div class="banner">
<div class="middle-wrapper" >
<div class="middle-wrapper">
<div @click="defaulttimeline">
<img :src="icon_url" class="appicon" style="width:100px; height:100px">
</div>
@ -70,7 +70,7 @@
name: "FirAppInfosBase",
data() {
return {
icon_url:"",
icon_url: "",
appinfos: {},
master_release: {},
allapp: [],
@ -80,41 +80,45 @@
}
},
methods: {
setfunactive(item,index){
setfunactive(item, index) {
for (let key in this.$refs) {
if(key === item){
if (key === item) {
this.$refs[key].classList.add('active');
this.$store.dispatch('doappInfoIndex', [[index, index], [index, index]]);
}else {
} else {
this.$refs[key].classList.remove('active');
}
}
},
appDownload() {
this.$router.push({name: 'FirDownload', params: { short: this.appinfos.short }})
this.$router.push({name: 'FirDownload', params: {short: this.appinfos.short}})
},
defaulttimeline() {
this.setfunactive('timeline',5);
this.setfunactive('timeline', 5);
this.$router.push({name: 'FirAppInfostimeline'});
},
baseinfo() {
this.setfunactive('baseinfo',18);
this.setfunactive('baseinfo', 18);
this.$router.push({name: 'FirAppInfosbaseinfo'});
},
security(){
this.setfunactive('security',31);
security() {
this.setfunactive('security', 31);
this.$router.push({name: 'FirAppInfossecurity'});
},
combo() {
this.setfunactive('combo',44);
this.setfunactive('combo', 44);
this.$router.push({name: 'FirAppInfoscombo'});
},
devices(){
this.setfunactive('devices',57);
if(this.appinfos.issupersign){
this.$router.push({"name":'FirSuperSignBase', params: {act: "useddevices"},query:{bundleid:this.appinfos.bundle_id}})
}else {
devices() {
this.setfunactive('devices', 57);
if (this.appinfos.issupersign) {
this.$router.push({
"name": 'FirSuperSignBase',
params: {act: "useddevices"},
query: {bundleid: this.appinfos.bundle_id}
})
} else {
this.$router.push({name: 'FirAppInfosdevices'});
}
},
@ -131,15 +135,13 @@
return ftype
},
},
computed: {
}, mounted() {
computed: {}, mounted() {
getappinfos(data => {
if (data.code === 1000) {
this.appinfos = data.data;
this.master_release = data.data.master_release;
this.$store.dispatch("getUser",data.userinfo);
this.$store.dispatch("getUser", data.userinfo);
this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos);
} else if (data.code === 1003) {
@ -151,11 +153,11 @@
}, {
"app_id": this.$route.params.id
});
if(this.$store.state.currentapp.master_release){
if (this.$store.state.currentapp.master_release) {
this.icon_url = this.$store.state.currentapp.master_release.icon_url
}
},watch:{
'$store.state.currentapp.master_release.icon_url':function () {
}, watch: {
'$store.state.currentapp.master_release.icon_url': function () {
this.icon_url = this.$store.state.currentapp.master_release.icon_url
}
}

@ -59,7 +59,7 @@
<script>
import {deleteapp, getappinfos, getapppicurl, uploadimgs, updateapp, getuploadurl} from "../restful"
import { uploadaliyunoss, uploadlocalstorage, uploadqiniuoss} from "../utils";
import {uploadaliyunoss, uploadlocalstorage, uploadqiniuoss} from "../utils";
export default {
name: "FirAppInfosbaseinfo",
@ -67,12 +67,12 @@
return {
currentapp: {},
imageUrl: "",
uploadconf:{},
uploadprocess:0
uploadconf: {},
uploadprocess: 0
}
},
methods: {
updateimgs(certinfo){
updateimgs(certinfo) {
uploadimgs(data => {
if (data.code === 1000) {
// eslint-disable-next-line no-console
@ -80,37 +80,37 @@
this.$message.success('上传成功');
this.updateinfo();
}else {
} else {
this.$message.error('更新失败');
}
},{'methods':'PUT','data':{'certinfo':certinfo}});
}, {'methods': 'PUT', 'data': {'certinfo': certinfo}});
},
uploadtostorage(file,certinfo){
uploadtostorage(file, certinfo) {
if(certinfo.storage === 1){
if (certinfo.storage === 1) {
// eslint-disable-next-line no-unused-vars,no-unreachable
uploadqiniuoss(file,certinfo,this,res=>{
uploadqiniuoss(file, certinfo, this, res => {
this.updateimgs(certinfo);
},process=>{
}, process => {
this.uploadprocess = process;
})
}else if(certinfo.storage === 2){
} else if (certinfo.storage === 2) {
// eslint-disable-next-line no-unused-vars
uploadaliyunoss(file,certinfo,this,res=>{
uploadaliyunoss(file, certinfo, this, res => {
this.updateimgs(certinfo);
},process=>{
}, process => {
this.uploadprocess = process;
});
}else {
} else {
//
certinfo.upload_url = getuploadurl();
// eslint-disable-next-line no-unused-vars,no-unreachable
uploadlocalstorage(file,certinfo,this,res=>{
uploadlocalstorage(file, certinfo, this, res => {
this.updateimgs(certinfo);
},process=>{
}, process => {
this.uploadprocess = process;
})
}
@ -146,13 +146,13 @@
});
// alert('APP',this.delapp.name)
},
updateinfo(){
updateinfo() {
getappinfos(data => {
if (data.code === 1000) {
this.appinfos = data.data;
this.master_release = data.data.master_release;
this.$store.dispatch("getUser",data.userinfo);
this.$store.dispatch("getUser", data.userinfo);
this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos);
@ -170,8 +170,8 @@
updateapp(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
}else {
this.$message.error('操作失败,'+data.msg);
} else {
this.$message.error('操作失败,' + data.msg);
}
}, {
"app_id": this.currentapp.app_id,
@ -184,19 +184,21 @@
},
beforeAvatarUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if(file.type === 'image/jpeg' || file.type === 'image/png'|| file.type === 'image/jpg'){
if (file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg') {
if (isLt2M) {
uploadimgs(data => {
if (data.code === 1000) {
let certinfo=data.data;
this.uploadtostorage(file,certinfo);
}
},{'methods':'GET','data':{'app_id':this.currentapp.app_id,'upload_key':file.name,'ftype':'app'}});
let certinfo = data.data;
this.uploadtostorage(file, certinfo);
}
else{
}, {
'methods': 'GET',
'data': {'app_id': this.currentapp.app_id, 'upload_key': file.name, 'ftype': 'app'}
});
} else {
this.$message.error('上传应用图片大小不能超过 2MB!');
}
}else {
} else {
this.$message.error('上传应用图片只能是 JPG/PNG/JPEG 格式!');
}
@ -205,7 +207,7 @@
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[18, 18], [18, 18]]);
if(!this.currentapp.app_id){
if (!this.currentapp.app_id) {
this.currentapp = this.$store.state.currentapp;
}
this.uploadconf = {
@ -216,8 +218,8 @@
'$store.state.currentapp': function () {
this.currentapp = this.$store.state.currentapp;
}
},computed:{
getuppicurl(){
}, computed: {
getuppicurl() {
return getapppicurl(this.currentapp.app_id)
}
}

@ -93,7 +93,7 @@
hmaster_release: {},
comboapplists: [],
orgapplists: [],
comboapp:{"master_release":""}
comboapp: {"master_release": ""}
}
}, methods: {
searchapps() {
@ -202,10 +202,10 @@
} else {
this.$router.push({name: 'FirLogin'});
}
}, {"type": type,"page":1,"size":999,"act":'combo'});
}, {"type": type, "page": 1, "size": 999, "act": 'combo'});
}
}, filters: {
geticon_url(app){
geticon_url(app) {
return app.icon_url
},
getapptype: function (type) {

@ -35,12 +35,10 @@
udidlists: []
}
},
methods: {
},
methods: {},
mounted() {
this.$store.dispatch('doappInfoIndex', [[57, 57], [57, 57]]);
if(!this.currentapp.app_id){
if (!this.currentapp.app_id) {
this.currentapp = this.$store.state.currentapp;
this.udidlists = this.currentapp.master_release.udid;
}
@ -50,8 +48,7 @@
this.currentapp = this.$store.state.currentapp;
this.udidlists = this.currentapp.master_release.udid;
}
},computed:{
}
}, computed: {}
}
</script>

@ -20,7 +20,7 @@
</el-form-item>
<el-form-item label-width="160px" label="是否开启访问密码" >
<el-form-item label-width="160px" label="是否开启访问密码">
<el-tooltip placement="top">
<div slot="content">
@ -54,7 +54,9 @@
inactive-value="off">
</el-switch>
</el-tooltip>
<el-button @click="clean_app" v-if="currentapp.issupersign === 0 && currentapp.count !== 0" style="margin-left: 20px" size="small" type="info" plain>清理开发者账户脏数据</el-button>
<el-button @click="clean_app" v-if="currentapp.issupersign === 0 && currentapp.count !== 0"
style="margin-left: 20px" size="small" type="info" plain>清理开发者账户脏数据
</el-button>
</el-form-item>
@ -66,7 +68,7 @@
</template>
<script>
import { updateapp, } from "../restful"
import {updateapp,} from "../restful"
import {deepCopy} from "../utils";
export default {
@ -75,31 +77,31 @@
return {
currentapp: {},
orgcurrentapp: {},
downtip:{'msg':''},
supersign:{'msg':''},
passwordtip:{'msg':''},
passwordflag:false,
showdownloadflag:false,
showsupersignflag:false,
clecount:0
downtip: {'msg': ''},
supersign: {'msg': ''},
passwordtip: {'msg': ''},
passwordflag: false,
showdownloadflag: false,
showsupersignflag: false,
clecount: 0
}
},
methods: {
clean_app(){
this.saveappinfo({"clean":true});
clean_app() {
this.saveappinfo({"clean": true});
this.currentapp.count = 0;
},
saveappinfo(data) {
updateapp(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
}else {
this.$message.error('操作失败,'+data.msg);
} else {
this.$message.error('操作失败,' + data.msg);
this.currentapp=deepCopy(this.orgcurrentapp);
this.passwordflag=false;
this.showdownloadflag=false;
this.showsupersignflag=false;
this.currentapp = deepCopy(this.orgcurrentapp);
this.passwordflag = false;
this.showdownloadflag = false;
this.showsupersignflag = false;
this.setbuttondefault(this.currentapp);
}
@ -108,57 +110,57 @@
"data": data
});
},
setbuttondefaltpass(currentapp){
if(currentapp.password === ''){
this.passwordtip.val='off';
setbuttondefaltpass(currentapp) {
if (currentapp.password === '') {
this.passwordtip.val = 'off';
this.showpasswordevent("off");
}else {
this.passwordtip.val='on';
} else {
this.passwordtip.val = 'on';
this.showpasswordevent("on");
}
this.passwordflag=true;
this.passwordflag = true;
},
setbuttondefaltshow(currentapp){
if(currentapp.isshow === 1){
setbuttondefaltshow(currentapp) {
if (currentapp.isshow === 1) {
this.showdownloadevent("on");
this.downtip.val='on';
}else {
this.downtip.val = 'on';
} else {
this.showdownloadevent("off");
this.downtip.val='off';
this.downtip.val = 'off';
}
this.showdownloadflag=true;
this.showdownloadflag = true;
},
setbuttonsignshow(currentapp){
if(currentapp.issupersign === 1){
setbuttonsignshow(currentapp) {
if (currentapp.issupersign === 1) {
this.supersignevent("on");
this.supersign.val='on';
}else {
this.supersign.val = 'on';
} else {
this.supersignevent("off");
this.supersign.val='off';
this.supersign.val = 'off';
}
this.showsupersignflag=true;
this.showsupersignflag = true;
},
setbuttondefault(currentapp){
setbuttondefault(currentapp) {
this.setbuttondefaltpass(currentapp);
this.setbuttondefaltshow(currentapp);
this.setbuttonsignshow(currentapp);
},
passwordswitch(state){
this.passwordflag=false;
passwordswitch(state) {
this.passwordflag = false;
this.showpasswordevent(state);
this.passwordtip.val=state;
this.passwordflag=true;
this.passwordtip.val = state;
this.passwordflag = true;
},
setaccesspassword(){
setaccesspassword() {
this.$prompt('', '请设置访问密码', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal:false,
inputValue:`${this.currentapp.password}`,
}).then(({ value }) => {
value = value.replace(/\s+/g,"");
if(this.currentapp.password === value ) {
if(value === ''){
closeOnClickModal: false,
inputValue: `${this.currentapp.password}`,
}).then(({value}) => {
value = value.replace(/\s+/g, "");
if (this.currentapp.password === value) {
if (value === '') {
this.$message({
type: 'success',
message: '访问密码未变'
@ -166,103 +168,103 @@
this.passwordflag = false;
this.setbuttondefaltpass(this.currentapp);
return
}else {
} else {
return
}
}
this.saveappinfo({
"password": value,
});
if(value === ''){
if (value === '') {
this.passwordswitch("off");
this.$message({
type: 'success',
message: '设置成功,取消密码访问'
});
}else {
this.passwordtip.msg='访问密码:' + value;
} else {
this.passwordtip.msg = '访问密码:' + value;
this.$message({
type: 'success',
message: '设置成功,访问密码是: ' + value
});
}
this.currentapp.password=value;
this.currentapp.password = value;
this.$store.dispatch('doucurrentapp', this.currentapp)
}).catch(() => {
if(this.currentapp.password === ''){
if (this.currentapp.password === '') {
this.passwordswitch("off")
}
});
},
showdownloadevent(newval){
if(newval === "on"){
if(this.showdownloadflag){
showdownloadevent(newval) {
if (newval === "on") {
if (this.showdownloadflag) {
this.saveappinfo({
"isshow": 1,
});
this.currentapp.isshow=1;
this.currentapp.isshow = 1;
}
this.downtip.msg='下载页对所有人可见';
}else {
if(this.showdownloadflag){
this.downtip.msg = '下载页对所有人可见';
} else {
if (this.showdownloadflag) {
this.saveappinfo({
"isshow": 0,
});
this.currentapp.isshow=0;
this.currentapp.isshow = 0;
}
this.downtip.msg = '下载页不可见'
}
},
supersignevent(newval){
if(newval === "on"){
if(this.showsupersignflag){
supersignevent(newval) {
if (newval === "on") {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 1,
});
this.currentapp.issupersign=1;
this.currentapp.issupersign = 1;
}
this.supersign.msg='已经开启超级签名';
}else {
if(this.showsupersignflag){
this.supersign.msg = '已经开启超级签名';
} else {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 0,
});
this.currentapp.issupersign=0;
this.currentapp.issupersign = 0;
}
this.supersign.msg = '关闭'
}
},
showpasswordevent(newval){
if(newval === "on"){
if(this.passwordflag){
showpasswordevent(newval) {
if (newval === "on") {
if (this.passwordflag) {
this.setaccesspassword()
}else {
this.passwordtip.msg='访问密码:' + this.currentapp.password ;
} else {
this.passwordtip.msg = '访问密码:' + this.currentapp.password;
}
}else {
if(this.passwordflag){
} else {
if (this.passwordflag) {
this.saveappinfo({
"password": '',
});
}
this.currentapp.password='';
this.currentapp.password = '';
this.$store.dispatch('doucurrentapp', this.currentapp);
this.passwordtip.msg='无访问密码'
this.passwordtip.msg = '无访问密码'
}
},
appinit(){
appinit() {
this.currentapp = this.$store.state.currentapp;
this.passwordflag=false;
this.showdownloadflag=false;
this.showsupersignflag=false;
this.passwordflag = false;
this.showdownloadflag = false;
this.showsupersignflag = false;
this.setbuttondefault(this.currentapp);
this.orgcurrentapp=deepCopy(this.currentapp)
this.orgcurrentapp = deepCopy(this.currentapp)
}
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[31, 31], [31, 31]]);
if(!this.currentapp.app_id){
if (!this.currentapp.app_id) {
this.appinit();
}
},
@ -270,8 +272,7 @@
'$store.state.currentapp': function () {
this.appinit();
},
},computed:{
}
}, computed: {}
}
</script>

@ -14,7 +14,7 @@
<span slot="footer" class="dialog-footer">
<el-button @click="willDeleteApp = false"> </el-button>
<!-- <el-button v-if="delapp.issupersign" type="danger" @click="$router.push({name: 'FirAppInfossecurity', params: {id: delapp.app_id}})"> </el-button>-->
<!-- <el-button v-if="delapp.issupersign" type="danger" @click="$router.push({name: 'FirAppInfossecurity', params: {id: delapp.app_id}})"> </el-button>-->
<el-button type="danger" @click="delApp"> </el-button>
</span>
</el-dialog>
@ -371,7 +371,8 @@
<i class="icon-eye el-icon-view"></i> 预览
</el-button>
<el-button v-if="r.issupersign" @click="DeleteApp(r)" class="btn btn-remove" icon="el-icon-loading"
<el-button v-if="r.issupersign" @click="DeleteApp(r)" class="btn btn-remove"
icon="el-icon-loading"
circle></el-button>
<el-button v-else @click="DeleteApp(r)" class="btn btn-remove" icon="el-icon-delete"
@ -717,17 +718,6 @@
getDelappTitle() {
return `删除应用 ${this.delapp.name}`
},
getBH: function () {
let sch = this.$refs.appmain.scrollHeight;
if (sch === 0 || sch < window.innerHeight) {
sch = window.innerHeight;
} else {
sch += 280;
}
return sch;
}
},
filters: {
getiOStype: function (type) {
@ -760,7 +750,7 @@
return ftype
},
make_icon_url(icon_url) {
if(icon_url){
if (icon_url) {
if (!icon_url.startsWith("http")) {
return location.origin + icon_url
} else {
@ -781,7 +771,6 @@
}, mounted() {
this.$store.dispatch('doucurrentapp', {});
this.getappsFun({});
// window.addEventListener('scroll',this.auto_load);
},
destroyed() {
window.removeEventListener('scroll', this.auto_load, false);

@ -31,12 +31,15 @@
<span id="qrcode" class="qrcode">
</span>
</div>
<p v-if="currentappinfo.issupersign" class="scan-tips wrapper icon-warp" >超级签</p>
<p v-else class="scan-tips wrapper icon-warp" >{{ mcurrentappinfo.release_type|getiOStype }}</p>
<p v-if="currentappinfo.issupersign" class="scan-tips wrapper icon-warp">超级签</p>
<p v-else class="scan-tips wrapper icon-warp">{{
mcurrentappinfo.release_type|getiOStype }}</p>
<h1 class="name wrapper">
<span class="icon-warp" style="margin-left:0px">
<i v-if="currentappinfo.type === 0 && agent !== ''" class="iconfont icon-android2"/>
<i v-if="currentappinfo.type === 1 && agent !== ''" class="iconfont icon-ios"/>
<i v-if="currentappinfo.type === 0 && agent !== ''"
class="iconfont icon-android2"/>
<i v-if="currentappinfo.type === 1 && agent !== ''"
class="iconfont icon-ios"/>
{{ currentappinfo.name }}
</span>
@ -46,7 +49,8 @@
class="text-black">{{ full_url }}</span></p>
<div class="release-info">
<p>{{ mcurrentappinfo.app_version }}Build {{ mcurrentappinfo.build_version }}- {{ mcurrentappinfo.binary_size }}</p>
<p>{{ mcurrentappinfo.app_version }}Build {{ mcurrentappinfo.build_version }}-
{{ mcurrentappinfo.binary_size }}</p>
<p>更新于{{ mcurrentappinfo.created_time |formatTime}}</p>
<p class="version" v-if="mcurrentappinfo.changelog && agent === ''">
更新日志{{ mcurrentappinfo.changelog }}
@ -59,15 +63,24 @@
</el-button>
<button type="button" v-else-if="wrong">{{ msg }}</button>
<div v-else >
<button v-if="isdownload" disabled="" class="loading" style="min-width: 42px; width: 42px; padding: 21px 0; border-top-color: transparent; border-left-color: transparent;">&nbsp;</button>
<div v-else>
<div v-if="currentappinfo.need_password" style="margin:0 auto; width:166px">
<el-input prefix-icon="el-icon-lock" clearable="true" placeholder="请输入密码" v-model="password" icon="el-icon-loadings" type="primary" :underline="false"> </el-input>
<button v-if="isdownload" disabled="" class="loading"
style="min-width: 42px; width: 42px; padding: 21px 0; border-top-color: transparent; border-left-color: transparent;">
&nbsp;
</button>
<div v-else>
<div v-if="currentappinfo.need_password"
style="margin:0 auto; width:166px">
<el-input prefix-icon="el-icon-lock" clearable="true"
placeholder="请输入密码" v-model="password"
icon="el-icon-loadings" type="primary"
:underline="false"></el-input>
</div>
<el-divider v-if="currentappinfo.need_password"></el-divider>
<button @click="download" >
<el-link icon="el-icon-loadings" type="primary" :underline="false"> 下载安装 </el-link>
<button @click="download">
<el-link icon="el-icon-loadings" type="primary" :underline="false">
下载安装
</el-link>
</button>
</div>
@ -149,7 +162,7 @@
<script>
import QRCode from 'qrcodejs2'
import {getShortAppinfo,getdownloadurl} from '../restful'
import {getShortAppinfo, getdownloadurl} from '../restful'
export default {
name: "FirDownload",
@ -159,56 +172,55 @@
iscomboappinfo: {},
mcurrentappinfo: {},
miscomboappinfo: {},
tmpinfo:{},
full_url:"",
tmpinfo: {},
full_url: "",
agent: '',
wrong: false,
msg: '',
password:'',
dchoice:false,
downloadurl:"",
isdownload:false,
udid:"",
password: '',
dchoice: false,
downloadurl: "",
isdownload: false,
udid: "",
}
}, methods: {
download() {
if( this.currentappinfo.app_id){
if (this.currentappinfo.app_id) {
this.isdownload = true;
getdownloadurl(res=>{
if(res.code === 1000){
if(res.data.download_url === ""){
window.location.href=this.full_url;
getdownloadurl(res => {
if (res.code === 1000) {
if (res.data.download_url === "") {
window.location.href = this.full_url;
return
}
if(this.currentappinfo.type === 1){
if(this.currentappinfo.issupersign){
if(this.$route.query.udid && this.udid === this.$route.query.udid){
if(this.agent !== ''){
if (this.currentappinfo.type === 1) {
if (this.currentappinfo.issupersign) {
if (this.$route.query.udid && this.udid === this.$route.query.udid) {
if (this.agent !== '') {
let download_url = res.data.download_url;
this.downloadurl="itms-services://?action=download-manifest&url="+encodeURIComponent(download_url);
this.downloadurl = "itms-services://?action=download-manifest&url=" + encodeURIComponent(download_url);
}
}else {
if(this.agent !== ''){
} else {
if (this.agent !== '') {
this.downloadurl = res.data.download_url;
}
}
}else {
} else {
let download_url = res.data.download_url;
this.downloadurl="itms-services://?action=download-manifest&url="+encodeURIComponent(download_url);
this.downloadurl = "itms-services://?action=download-manifest&url=" + encodeURIComponent(download_url);
}
}else{
if(this.agent !== ''){
} else {
if (this.agent !== '') {
this.downloadurl = res.data.download_url;
}
}
window.location.href=this.downloadurl;
}
else {
window.location.href = this.downloadurl;
} else {
this.isdownload = false;
this.password='';
this.password = '';
this.$message({
message:"密码错误,或者下载链接失效",
message: "密码错误,或者下载链接失效",
type: 'error',
});
}
@ -217,8 +229,8 @@
'token': this.mcurrentappinfo.download_token,
'short': this.currentappinfo.short,
'release_id': this.mcurrentappinfo.release_id,
'password':this.password,
'udid':this.udid,
'password': this.password,
'udid': this.udid,
},
'app_id': this.currentappinfo.app_id
})
@ -232,77 +244,77 @@
})
},
getDownloadTokenFun() {
let params={ "short": this.$route.params.short ,"time":new Date().getTime()};
if(this.$route.query.release_id){
params["release_id"]=this.$route.query.release_id
let params = {"short": this.$route.params.short, "time": new Date().getTime()};
if (this.$route.query.release_id) {
params["release_id"] = this.$route.query.release_id
}
if(this.$route.query.udid){
params["udid"]=this.$route.query.udid
}else {
params["udid"]=""
if (this.$route.query.udid) {
params["udid"] = this.$route.query.udid
} else {
params["udid"] = ""
}
getShortAppinfo(data => {
if (data.code === 1000) {
this.udid = data.udid;
if(!data.data.master_release.release_id){
if (!data.data.master_release.release_id) {
this.$message({
message:"该 release 版本不存在,请检查",
message: "该 release 版本不存在,请检查",
type: 'error',
duration:0
duration: 0
});
return
}
if(this.agent === "android"){
if (this.agent === "android") {
// iOS
if(data.data.type === 1){
if(data.data.has_combo){
if(!params.release_id){
if (data.data.type === 1) {
if (data.data.has_combo) {
if (!params.release_id) {
this.currentappinfo = data.data.has_combo;
this.mcurrentappinfo = data.data.has_combo.master_release;
}else {
} else {
this.wrong = true;
this.msg = '该 release 版本只能在苹果设备使用';
}
this.iscomboappinfo = data.data;
this.miscomboappinfo = data.data.master_release;
}else {
} else {
this.wrong = true;
this.msg = '苹果应用不支持安卓设备';
this.dchoice=true;
this.dchoice = true;
}
}else {
this.dchoice=true;
} else {
this.dchoice = true;
}
}else if(this.agent === "apple"){
if(data.data.type === 0){
if(data.data.has_combo){
if(!params.release_id){
} else if (this.agent === "apple") {
if (data.data.type === 0) {
if (data.data.has_combo) {
if (!params.release_id) {
this.currentappinfo = data.data.has_combo;
this.mcurrentappinfo = data.data.has_combo.master_release;
}else {
} else {
this.wrong = true;
this.msg = '该 release 版本只能在安卓设备使用';
}
this.iscomboappinfo = data.data;
this.miscomboappinfo = data.data.master_release;
}else {
} else {
this.wrong = true;
this.msg = '安卓应用不支持苹果设备';
this.dchoice=true;
this.dchoice = true;
}
}else {
this.dchoice=true;
} else {
this.dchoice = true;
}
}else {
this.dchoice=true;
} else {
this.dchoice = true;
}
if(this.dchoice){
if (this.dchoice) {
this.currentappinfo = data.data;
this.mcurrentappinfo = data.data.master_release;
if (this.currentappinfo.has_combo) {
@ -311,20 +323,19 @@
}
}
if(this.agent !== ''){
this.miscomboappinfo={};
if (this.agent !== '') {
this.miscomboappinfo = {};
this.iscomboappinfo = {};
}
document.title = this.currentappinfo.name+'下载';
if(this.mcurrentappinfo.binary_url && this.agent !=='' && this.wrong === false){
window.location.href=this.mcurrentappinfo.binary_url
}
document.title = this.currentappinfo.name + '下载';
if (this.mcurrentappinfo.binary_url && this.agent !== '' && this.wrong === false) {
window.location.href = this.mcurrentappinfo.binary_url
}
else {
} else {
this.$message({
message: data.msg,
type: 'error',
duration:0
duration: 0
});
}
}, params)
@ -376,20 +387,20 @@
this.getDownloadTokenFun();
this.full_url = location.href.split("?")[0];
this.qrcode();
},filters:{
}, filters: {
getiOStype: function (type) {
let ftype = '';
if (type === 1) {
ftype = '内测版'
} else if(type === 2) {
} else if (type === 2) {
ftype = '企业版'
}
return ftype
},
formatTime:function (stime) {
if(stime){
formatTime: function (stime) {
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0]+" "+stime[1]
return stime[0] + " " + stime[1]
}
}
}
@ -950,7 +961,7 @@
color: #fff
}
.main > header .actions button a{
.main > header .actions button a {
color: #fff
}
@ -1406,7 +1417,6 @@
}
.app_bottom_fixed img {
width: 100%;
max-height: 80px

@ -15,7 +15,9 @@
</el-col>
<el-col :span="2" :push="4">
<div class="block"><el-avatar :size="66" :src="$store.state.userinfo.head_img"></el-avatar></div>
<div class="block">
<el-avatar :size="66" :src="$store.state.userinfo.head_img"></el-avatar>
</div>
</el-col>
<el-col :span="4" :push="4">
<el-dropdown style="padding-top: 12px;" @command="handleCommand">
@ -44,7 +46,8 @@
</template>
<script>
import {logout} from '../restful'
import {logout} from '../restful'
export default {
name: "FirHeader",
data() {
@ -58,14 +61,13 @@ import {logout} from '../restful'
this.$router.push({name: 'FirUserProfileInfo'})
} else if (command === 'chpasswd') {
this.$router.push({name: 'FirUserProfileChangePwd'})
}else if (command === 'storage') {
} else if (command === 'storage') {
this.$router.push({name: 'FirUserProfileStorage'})
}else if (command === 'supersign') {
} else if (command === 'supersign') {
this.$store.dispatch('doucurrentapp', {});
this.$router.push({"name":'FirSuperSignBase', params: {act: "iosdeveloper"}})
this.$router.push({"name": 'FirSuperSignBase', params: {act: "iosdeveloper"}})
}
else if(command === 'exit'){
} else if (command === 'exit') {
logout(data => {
if (data.code === 1000) {
this.$message.success("退出成功");

@ -30,9 +30,9 @@
<el-button type="danger" @click="onSubmit">登录</el-button>
</el-form-item>
<!-- <el-form-item>-->
<!-- <el-button type="primary" @click="onRegister" plain>注册</el-button>-->
<!-- </el-form-item>-->
<!-- <el-form-item>-->
<!-- <el-button type="primary" @click="onRegister" plain>注册</el-button>-->
<!-- </el-form-item>-->
</el-form>
@ -74,7 +74,7 @@
this.$cookies.set("token", data['token']);
this.$cookies.set("username", data.userinfo.username);
this.$cookies.set("first_name", data.userinfo.first_name);
this.$store.dispatch("get_user",data.userinfo);
this.$store.dispatch("get_user", data.userinfo);
set_auth_token();
this.$router.push({name: 'FirApps'})
} else {

@ -1,13 +1,14 @@
<template>
<el-main>
<el-dialog :title="title" :visible.sync="dialogaddDeveloperVisible" :destroy-on-close="true" :close-on-click-modal="false" style="text-align:center">
<el-dialog :title="title" :visible.sync="dialogaddDeveloperVisible" :destroy-on-close="true"
:close-on-click-modal="false" style="text-align:center">
<el-form ref="storageinfoform" :model="editdeveloperinfo"
label-width="80px" style="margin:0 auto;">
<el-form-item label-width="110px" label="APP_ID">
<el-input :disabled='isedit' v-model="editdeveloperinfo.email" ></el-input>
<el-input :disabled='isedit' v-model="editdeveloperinfo.email"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="password">
@ -20,9 +21,16 @@
<el-form-item label-width="110px" label="备注">
<el-input v-model="editdeveloperinfo.description"></el-input>
</el-form-item>
<div style="" >
<el-button v-if="isedit && editdeveloperinfo.is_actived" @click="syncdevices">同步设备信息</el-button>
<el-button v-if="isedit && editdeveloperinfo.is_actived && !editdeveloperinfo.certid" @click="isocertcert">手动创建证书</el-button>
<div style="">
<el-button v-if="isedit && editdeveloperinfo.is_actived" size="small" @click="syncdevices">同步设备信息
</el-button>
<el-button v-if="isedit && editdeveloperinfo.is_actived && !editdeveloperinfo.certid" size="small"
@click="isocertcert">手动创建证书
</el-button>
<el-button v-if="isedit && editdeveloperinfo.is_actived" type="danger" size="small"
@click="activedeveloperFun(editdeveloperinfo,'checkauth')">开发者账户激活检测
</el-button>
<el-button @click="updateorcreate">保存</el-button>
<el-button @click="canceledit">取消</el-button>
</div>
@ -37,28 +45,40 @@
:visible.sync="codeactiveVisible"
width="30%"
center>
<div>
<el-input
placeholder="一般为6位数字"
v-model="authcode">
</el-input>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="codeactiveVisible = false"> </el-button>
<el-button type="primary" @click="codeactiveVisible = false"> </el-button>
<el-button type="primary" @click="inputcode"> </el-button>
</span>
</el-dialog>
<el-tabs v-model="activeName" type="border-card" @tab-click="handleClick" tab-position="top">
<el-tab-pane label="开发者账户" name="iosdeveloper">
<el-input
style="width: 30%;margin-right: 30px;margin-bottom: 10px"
v-model="appidseach"
clearable
placeholder="输入用户APPID" />
<el-button type="primary" icon="el-icon-search" @click="handleCurrentChange(pagination.currentPage)">搜索</el-button>
placeholder="输入用户APPID"/>
<el-button type="primary" icon="el-icon-search" @click="handleCurrentChange(pagination.currentPage)">
搜索
</el-button>
<div style="width: 40%;margin-right: 30px;float:right">
<el-link :underline="false">总设备量{{ developer_used_info.all_usable_number}} 已经使用平台{{ developer_used_info.all_use_number}} 其他{{developer_used_info.other_used_sum}} 还剩{{developer_used_info.all_usable_number- developer_used_info.flyapp_used_sum}} 可用</el-link>
<el-link :underline="false">总设备量{{ developer_used_info.all_usable_number}} 已经使用平台{{
developer_used_info.all_use_number}} 其他{{developer_used_info.other_used_sum}}
还剩{{developer_used_info.all_usable_number- developer_used_info.flyapp_used_sum}} 可用
</el-link>
<el-progress
type="line"
:color="developer_usedColor"
:text-inside="true" :stroke-width="18" :percentage="percentage" status="success"></el-progress>
:text-inside="true" :stroke-width="18" :percentage="percentage"
status="success"></el-progress>
</div>
<el-table
@ -76,7 +96,9 @@
<el-popover trigger="hover" placement="top">
<p>开发者账户已使用设备数: {{ scope.row.developer_used_number }}</p>
<p>开发者账户可用设备数: {{ 100-scope.row.developer_used_number }}</p>
<p>由于您设置可用设备数: {{ scope.row.usable_number}} ,所以现在可用设备数: {{ scope.row.usable_number-scope.row.developer_used_number > 0 ?scope.row.usable_number-scope.row.developer_used_number :0 }}</p>
<p>由于您设置可用设备数: {{ scope.row.usable_number}} ,所以现在可用设备数: {{
scope.row.usable_number-scope.row.developer_used_number > 0
?scope.row.usable_number-scope.row.developer_used_number :0 }}</p>
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{ scope.row.email }}</el-tag>
@ -89,21 +111,43 @@
prop="is_actived"
label="是否激活"
width="110">
<template slot-scope="scope" >
<template slot-scope="scope">
<el-button v-if="scope.row.is_actived === true" type="success" size="small">已激活</el-button>
<el-button v-else type="danger" size="small" @click="activedeveloperFun(scope.row)">点击激活</el-button>
<el-button v-else type="danger" size="small"
@click="activedeveloperFun(scope.row,'preactive')">点击激活
</el-button>
</template>
</el-table-column>
<el-table-column
prop="certid"
label="账户状态"
width="100">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p v-if="!scope.row.certid && scope.row.is_actived === true">开发证书不可用请在编辑中手动创建开发者证书</p>
<p v-if="!scope.row.certid && scope.row.is_actived !== true">请先激活开发者账户</p>
<p v-if="scope.row.certid && scope.row.is_actived === true">账户已经启用</p>
<div slot="reference" class="name-wrapper">
<el-button v-if="scope.row.certid " type="success" size="small">可用</el-button>
<el-button v-else type="danger" size="small">不可用</el-button>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column
prop="usable_number"
label="可用设备数"
label="可用设备"
width="100">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p>可用设备数: {{ scope.row.usable_number-scope.row.developer_used_number > 0 ?scope.row.usable_number-scope.row.developer_used_number :0}}</p>
<p>可用设备数: {{ scope.row.usable_number-scope.row.developer_used_number > 0
?scope.row.usable_number-scope.row.developer_used_number :0}}</p>
<div slot="reference" class="name-wrapper">
<el-tag size="medium"> {{ scope.row.usable_number-scope.row.developer_used_number > 0 ?scope.row.usable_number-scope.row.developer_used_number :0 }}</el-tag>
<el-tag size="medium"> {{ scope.row.usable_number-scope.row.developer_used_number >
0 ?scope.row.usable_number-scope.row.developer_used_number :0 }}
</el-tag>
</div>
</el-popover>
</template>
@ -111,8 +155,8 @@
<el-table-column
prop="use_number"
label="已消耗设备数"
width="120">
label="设备消耗"
width="100">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p>开发者账户已使用设备数本平台: {{ scope.row.use_number }}</p>
@ -139,15 +183,17 @@
fixed="right"
label="操作"
width="150">
<template slot-scope="scope" >
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEditDeveloper(scope.row)">编辑</el-button>
@click="handleEditDeveloper(scope.row)">编辑
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDeleteDeveloper(scope.row)">删除</el-button>
@click="handleDeleteDeveloper(scope.row)">删除
</el-button>
</template>
</el-table-column>
@ -155,24 +201,50 @@
</el-tab-pane>
<el-tab-pane label="添加开发者" name="adddeveloper">添加开发者</el-tab-pane>
<el-tab-pane label="添加开发者" name="adddeveloper" style="text-align:center">
<el-form ref="storageinfoform" :model="editdeveloperinfo"
label-width="80px" style="margin:0 auto;">
<el-form-item label-width="110px" label="APP_ID">
<el-input :disabled='isedit' v-model="editdeveloperinfo.email"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="password">
<el-input v-model="editdeveloperinfo.password" :placeholder="placeholder"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="设备数量">
<el-input v-model="editdeveloperinfo.usable_number"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="备注">
<el-input v-model="editdeveloperinfo.description"></el-input>
</el-form-item>
<div style="">
<el-button @click="updateorcreate">添加</el-button>
<el-button @click="canceledit">取消</el-button>
</div>
</el-form>
</el-tab-pane>
<el-tab-pane label="设备消耗" name="useddevices">
<el-input
style="width: 30%;margin-right: 30px;margin-bottom: 10px"
v-model="udidsearch"
clearable
placeholder="输入UDID" />
placeholder="输入UDID"/>
<el-input
style="width: 20%;margin-right: 30px;margin-bottom: 10px"
v-model="Bundleidsearch"
clearable
placeholder="输入BundleID" />
placeholder="输入BundleID"/>
<el-input
style="width: 20%;margin-right: 30px;margin-bottom: 10px"
v-model="appidseach"
clearable
placeholder="输入用户APPID" />
<el-button type="primary" icon="el-icon-search" @click="handleCurrentChange(pagination.currentPage)">搜索</el-button>
placeholder="输入用户APPID"/>
<el-button type="primary" icon="el-icon-search" @click="handleCurrentChange(pagination.currentPage)">
搜索
</el-button>
<el-table
:data="app_devices_lists"
@ -220,13 +292,15 @@
style="width: 30%;margin-right: 30px;margin-bottom: 10px"
v-model="udidsearch"
clearable
placeholder="输入UDID" />
placeholder="输入UDID"/>
<el-input
style="width: 20%;margin-right: 30px;margin-bottom: 10px"
v-model="Bundleidsearch"
clearable
placeholder="输入BundleID" />
<el-button type="primary" icon="el-icon-search" @click="handleCurrentChange(pagination.currentPage)">搜索</el-button>
placeholder="输入BundleID"/>
<el-button type="primary" icon="el-icon-search" @click="handleCurrentChange(pagination.currentPage)">
搜索
</el-button>
<el-table
@ -282,11 +356,12 @@
fixed="right"
label="操作"
width="80">
<template slot-scope="scope" >
<template slot-scope="scope">
<el-button
size="mini"
type="danger"
@click="udidDeleteFun(scope)">删除</el-button>
@click="udidDeleteFun(scope)">删除
</el-button>
</template>
</el-table-column>
</el-table>
@ -311,33 +386,34 @@
<script>
import {iosdeveloper, iosdevices, iosdevicesudid, userinfos} from "../restful";
import {removeAaary,IsNum} from "../utils";
import {removeAaary, IsNum} from "../utils";
export default {
name: "FirSuperSignBase",
data() {
return {
app_developer_lists:[],
app_devices_lists:[],
app_udid_lists:[],
activeName:"iosdeveloper",
udidsearch:"",
Bundleidsearch:"",
appidseach:"",
dialogaddDeveloperVisible:false,
title:"",
editdeveloperinfo:{},
isedit:false,
placeholder:"",
codeactiveVisible:false,
pagination:{"currentPage":1,"total":0,"pagesize":10},
developer_used_info:{"all_usable_number":0,"other_used_sum":0,"all_use_number":0},
percentage:0,
app_developer_lists: [],
app_devices_lists: [],
app_udid_lists: [],
activeName: "iosdeveloper",
udidsearch: "",
Bundleidsearch: "",
appidseach: "",
dialogaddDeveloperVisible: false,
title: "",
editdeveloperinfo: {},
isedit: false,
placeholder: "",
codeactiveVisible: false,
pagination: {"currentPage": 1, "total": 0, "pagesize": 10},
developer_used_info: {"all_usable_number": 0, "other_used_sum": 0, "all_use_number": 0},
percentage: 0,
authcode: "",
authemail: "",
}
},
methods: {
developer_usedColor(percentage){
developer_usedColor(percentage) {
if (percentage < 20) {
return '#6f7ad3';
} else if (percentage < 40) {
@ -347,75 +423,85 @@
} else if (percentage < 80) {
return '#e6a23c';
} else if(percentage<90){
} else if (percentage < 90) {
return '#E63918';
}else {
} else {
return '#F50346';
}
},
udidDeleteFun(scope){
this.iosdevicesudidFun('DELETE',{id:scope.row.id,aid:scope.row.app_id});
this.app_udid_lists=removeAaary(this.app_udid_lists,scope.row)
udidDeleteFun(scope) {
this.iosdevicesudidFun('DELETE', {id: scope.row.id, aid: scope.row.app_id});
this.app_udid_lists = removeAaary(this.app_udid_lists, scope.row)
},
handleSizeChange(val) {
this.pagination.pagesize=val;
this.get_data_from_tabname(this.activeName,{"size":this.pagination.pagesize,"page":this.pagination.currentPage})
this.pagination.pagesize = val;
this.get_data_from_tabname(this.activeName, {
"size": this.pagination.pagesize,
"page": this.pagination.currentPage
})
},
handleCurrentChange(val) {
this.pagination.currentPage=val;
this.get_data_from_tabname(this.activeName,{"size":this.pagination.pagesize,"page":this.pagination.currentPage})
},
syncdevices(){
this.iosdeveloperFun({"methods":"PUT","data":{"email":this.editdeveloperinfo.email,"act":"syncdevice"}});
this.pagination.currentPage = val;
this.get_data_from_tabname(this.activeName, {
"size": this.pagination.pagesize,
"page": this.pagination.currentPage
})
},
isocertcert(){
this.iosdeveloperFun({"methods":"PUT","data":{"email":this.editdeveloperinfo.email,"act":"ioscert"}});
syncdevices() {
this.iosdeveloperFun({
"methods": "PUT",
"data": {"email": this.editdeveloperinfo.email, "act": "syncdevice"}
});
},
inputcode(developer){
this.$prompt('请输入验证码', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal:false,
}).then(({ value }) => {
if(value.toString().length>5 && IsNum(value)){
this.iosdeveloperFun({"methods":"PUT","data":{"email":developer.email,"act":"nowactive","code":value.replace(/^\s+|\s+$/g, "")}});
}
}).catch(() => {
this.$message({
type: 'info',
message: '取消激活'
isocertcert() {
this.iosdeveloperFun({
"methods": "PUT",
"data": {"email": this.editdeveloperinfo.email, "act": "ioscert"}
});
},
inputcode() {
this.authcode = this.authcode.replace(/^\s+|\s+$/g, "");
if (this.authcode.toString().length > 5 && IsNum(this.authcode)) {
this.iosdeveloperFun({
"methods": "PUT",
"data": {"email": this.authemail, "act": "nowactive", "code": this.authcode}
});
this.authcode = "";
this.authemail = ""
}
},
activedeveloperFun(developer){
this.iosdeveloperFun({"methods":"PUT","data":{"email":developer.email,"act":"preactive"}});
this.inputcode(developer)
activedeveloperFun(developer, act) {
this.iosdeveloperFun({"methods": "PUT", "data": {"email": developer.email, "act": act}});
this.codeactiveVisible = true;
this.authemail = developer.email;
// this.inputcode(developer)
},
canceledit(){
this.dialogaddDeveloperVisible=false;
this.editdeveloperinfo={};
this.isedit=false;
this.placeholder=""
canceledit() {
this.dialogaddDeveloperVisible = false;
this.editdeveloperinfo = {};
this.isedit = false;
this.placeholder = ""
},
handleEditDeveloper(developer_info){
this.editdeveloperinfo=developer_info;
this.title='编辑开发者账户';
this.dialogaddDeveloperVisible=true;
this.isedit=true;
this.placeholder="为空表示不修改密码"
handleEditDeveloper(developer_info) {
this.editdeveloperinfo = developer_info;
this.title = '编辑开发者账户';
this.dialogaddDeveloperVisible = true;
this.isedit = true;
this.placeholder = "为空表示不修改密码"
},
updateorcreate(){
if(this.isedit){
this.iosdeveloperFun({"methods":"PUT","data":this.editdeveloperinfo})
}else {
if(this.editdeveloperinfo.email && this.editdeveloperinfo.password && this.editdeveloperinfo.usable_number ){
this.iosdeveloperFun({"methods":"POST","data":this.editdeveloperinfo});
}else {
updateorcreate() {
if (this.isedit) {
this.iosdeveloperFun({"methods": "PUT", "data": this.editdeveloperinfo})
} else {
if (this.editdeveloperinfo.email && this.editdeveloperinfo.password && this.editdeveloperinfo.usable_number) {
this.iosdeveloperFun({"methods": "POST", "data": this.editdeveloperinfo});
} else {
this.$message.warning("输入格式有误")
}
}
},
handleDeleteDeveloper(developer_info){
handleDeleteDeveloper(developer_info) {
this.$confirm('此操作会删除该苹果开发者账户下面的生成的证书等数据,可能会导致超级签包的闪退, 是否继续?', '警告', {
@ -423,7 +509,7 @@
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.iosdeveloperFun({"methods":"DELETE","data":{"email":developer_info.email}});
this.iosdeveloperFun({"methods": "DELETE", "data": {"email": developer_info.email}});
}).catch(() => {
this.$message({
type: 'info',
@ -431,29 +517,29 @@
});
});
},
get_data_from_tabname(tabname,data={}){
data.udid=this.udidsearch.replace(/^\s+|\s+$/g, "");
data.bundleid=this.Bundleidsearch.replace(/^\s+|\s+$/g, "");
data.appid=this.appidseach.replace(/^\s+|\s+$/g, "");
this.$router.push({"name":'FirSuperSignBase', params: {act: tabname}});
if(tabname === "useddevices"){
this.iosdevicesFun('GET',data)
}else if(tabname === "devicesudid"){
this.iosdevicesudidFun('GET',data)
}else if(tabname === "adddeveloper"){
this.title='新增私有开发者账户';
this.dialogaddDeveloperVisible=true;
}else if(tabname === "iosdeveloper"){
this.iosdeveloperFun({"methods":"GET","data":data})
get_data_from_tabname(tabname, data = {}) {
data.udid = this.udidsearch.replace(/^\s+|\s+$/g, "");
data.bundleid = this.Bundleidsearch.replace(/^\s+|\s+$/g, "");
data.appid = this.appidseach.replace(/^\s+|\s+$/g, "");
this.$router.push({"name": 'FirSuperSignBase', params: {act: tabname}});
if (tabname === "useddevices") {
this.iosdevicesFun('GET', data)
} else if (tabname === "devicesudid") {
this.iosdevicesudidFun('GET', data)
} else if (tabname === "adddeveloper") {
// this.title='';
// this.dialogaddDeveloperVisible=true;
} else if (tabname === "iosdeveloper") {
this.iosdeveloperFun({"methods": "GET", "data": data})
}
},
// eslint-disable-next-line no-unused-vars
handleClick(tab, event) {
this.pagination={"currentPage":1,"total":0,"pagesize":10};
this.pagination = {"currentPage": 1, "total": 0, "pagesize": 10};
this.get_data_from_tabname(tab.name);
},
// eslint-disable-next-line no-unused-vars
deviceformatter(row, column){
deviceformatter(row, column) {
let stime = row.created_time;
if (stime) {
stime = stime.split(".")[0].split("T");
@ -462,7 +548,7 @@
return '';
},
// eslint-disable-next-line no-unused-vars
formatter(row, column){
formatter(row, column) {
let stime = row.updated_time;
if (stime) {
stime = stime.split(".")[0].split("T");
@ -470,77 +556,82 @@
} else
return '';
},
iosdevicesFun(methods,data){
iosdevices(data=>{
if(data.code === 1000){
iosdevicesFun(methods, data) {
iosdevices(data => {
if (data.code === 1000) {
this.app_devices_lists = data.data;
this.pagination.total=data.count;
this.pagination.total = data.count;
}
},{
"methods":methods,"data":data
}, {
"methods": methods, "data": data
})
},
iosdeveloperFun(params){
iosdeveloper(data=>{
if(data.code === 1000){
iosdeveloperFun(params) {
iosdeveloper(data => {
if (data.code === 1000) {
this.app_developer_lists = data.data;
this.pagination.total=data.count;
if(data.use_num){
this.developer_used_info=data.use_num;
if(this.developer_used_info.all_usable_number !==0){
this.percentage=parseInt(this.developer_used_info.flyapp_used_sum*100/this.developer_used_info.all_usable_number);
this.pagination.total = data.count;
if (data.use_num) {
this.developer_used_info = data.use_num;
if (this.developer_used_info.all_usable_number !== 0) {
this.percentage = parseInt(this.developer_used_info.flyapp_used_sum * 100 / this.developer_used_info.all_usable_number);
}
}
if(this.dialogaddDeveloperVisible){
if (this.dialogaddDeveloperVisible) {
this.canceledit();
this.$message.success("操作成功");
this.activeName="iosdeveloper";
this.editdeveloperinfo={};
this.activeName = "iosdeveloper";
this.editdeveloperinfo = {};
}
if (!this.edit && this.editdeveloperinfo.email) {
this.$message.success("添加成功");
this.activeName = "iosdeveloper";
this.editdeveloperinfo = {};
}
else if(data.code === 1008){
} else if (data.code === 1008) {
this.$message.error(data.msg);
}
else {
} else {
this.$message.error("操作失败")
}
},params)
if (this.codeactiveVisible) {
this.codeactiveVisible = false;
}
}, params)
},
iosdevicesudidFun(action,data){
iosdevicesudid(data=>{
if(data.code === 1000){
if(action !== "DELETE"){
iosdevicesudidFun(action, data) {
iosdevicesudid(data => {
if (data.code === 1000) {
if (action !== "DELETE") {
this.app_udid_lists = data.data;
this.pagination.total=data.count;
this.pagination.total = data.count;
}
}
},{
"methods":action,"data":data
}, {
"methods": action, "data": data
})
},
getUserInfoFun(){
userinfos(data=>{
if(data.code === 1000){
this.$store.dispatch("getUser",data.data);
}else {
getUserInfoFun() {
userinfos(data => {
if (data.code === 1000) {
this.$store.dispatch("getUser", data.data);
} else {
this.$message.error("用户信息获取失败")
}
},{"methods":"GET"})
}, {"methods": "GET"})
},
}, mounted() {
this.getUserInfoFun();
if(this.$route.params.act){
let activeName=this.$route.params.act;
let activeName_list=["useddevices","devicesudid","adddeveloper","iosdeveloper"];
for(let index in activeName_list){
if(activeName_list[index] === activeName){
this.activeName=activeName;
let bundleid=this.$route.query.bundleid;
if(bundleid){
this.Bundleidsearch=bundleid;
if (this.$route.params.act) {
let activeName = this.$route.params.act;
let activeName_list = ["useddevices", "devicesudid", "adddeveloper", "iosdeveloper"];
for (let index in activeName_list) {
if (activeName_list[index] === activeName) {
this.activeName = activeName;
let bundleid = this.$route.query.bundleid;
if (bundleid) {
this.Bundleidsearch = bundleid;
}
this.get_data_from_tabname(activeName);
return

@ -70,104 +70,106 @@
data() {
return {
imageUrl: '',
userinfo:{},
uploadconf:{},
userinfo: {},
uploadconf: {},
}
},
methods: {
updateimgs(certinfo){
updateimgs(certinfo) {
uploadimgs(data => {
if (data.code === 1000) {
// eslint-disable-next-line no-console
console.log(data.data);
this.$message.success('上传成功');
this.updateUserInfo({"methods":'GET'});
this.updateUserInfo({"methods": 'GET'});
}else {
} else {
this.$message.error('更新失败');
}
},{'methods':'PUT','data':{'certinfo':certinfo}});
}, {'methods': 'PUT', 'data': {'certinfo': certinfo}});
},
uploadtostorage(file,certinfo){
uploadtostorage(file, certinfo) {
if(certinfo.storage === 1){
if (certinfo.storage === 1) {
// eslint-disable-next-line no-unused-vars,no-unreachable
uploadqiniuoss(file,certinfo,this,res=>{
uploadqiniuoss(file, certinfo, this, res => {
this.updateimgs(certinfo);
},process=>{
}, process => {
this.uploadprocess = process;
})
}else if(certinfo.storage === 2){
} else if (certinfo.storage === 2) {
// eslint-disable-next-line no-unused-vars
uploadaliyunoss(file,certinfo,this,res=>{
uploadaliyunoss(file, certinfo, this, res => {
this.updateimgs(certinfo);
},process=>{
}, process => {
this.uploadprocess = process;
});
}else {
} else {
//
certinfo.upload_url = getuploadurl();
// eslint-disable-next-line no-unused-vars,no-unreachable
uploadlocalstorage(file,certinfo,this,res=>{
uploadlocalstorage(file, certinfo, this, res => {
this.updateimgs(certinfo);
},process=>{
}, process => {
this.uploadprocess = process;
})
}
},
updateUserInfo(datainfo){
userinfos(data=>{
if(data.code === 1000){
updateUserInfo(datainfo) {
userinfos(data => {
if (data.code === 1000) {
this.userinfo = data.data;
this.$store.dispatch("getUser",data.data);
this.$store.dispatch("getUser", data.data);
this.$store.dispatch('doucurrentapp', {});
this.imageUrl = data.data.head_img;
if(datainfo.data){
if (datainfo.data) {
this.$message.success("更新成功")
}
}else {
} else {
this.$message.error("更新失败")
}
},datainfo)
}, datainfo)
},
update_name(){
this.updateUserInfo({"methods":'PUT','data':{"first_name":this.userinfo.first_name}});
update_name() {
this.updateUserInfo({"methods": 'PUT', 'data': {"first_name": this.userinfo.first_name}});
},
beforeAvatarUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if(file.type === 'image/jpeg' || file.type === 'image/png'|| file.type === 'image/jpg'){
if (file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg') {
if (isLt2M) {
uploadimgs(data => {
if (data.code === 1000) {
// eslint-disable-next-line no-console
console.log(data.data);
let certinfo=data.data;
this.uploadtostorage(file,certinfo);
let certinfo = data.data;
this.uploadtostorage(file, certinfo);
}
},{'methods':'GET','data':{'app_id':this.userinfo.uid,'upload_key':file.name,'ftype':'head'}});
}, {
'methods': 'GET',
'data': {'app_id': this.userinfo.uid, 'upload_key': file.name, 'ftype': 'head'}
});
return false;
}
else{
} else {
this.$message.error('上传头像图片大小不能超过 2MB!');
}
}else {
} else {
this.$message.error('上传头像图片只能是 JPG/PNG/JPEG 格式!');
}
return false;
},
setfunactive(item){
setfunactive(item) {
for (let key in this.$refs) {
if(key === item){
if (key === item) {
this.$refs[key].classList.add('active');
}else {
} else {
this.$refs[key].classList.remove('active');
}
}
@ -177,21 +179,19 @@
this.setfunactive('userinfo');
} else if (this.$store.state.userInfoIndex === 1) {
this.setfunactive('changepwd');
}else if (this.$store.state.userInfoIndex === 2) {
} else if (this.$store.state.userInfoIndex === 2) {
this.setfunactive('storage');
}
}
}, mounted() {
this.autoSetInfoIndex();
this.updateUserInfo({"methods":'GET'});
this.updateUserInfo({"methods": 'GET'});
}, watch: {
'$store.state.userInfoIndex': function () {
this.autoSetInfoIndex();
},
},filters:{
},computed:{
getuppicurl(){
}, filters: {}, computed: {
getuppicurl() {
return getuserpicurl()
}
}

@ -59,17 +59,19 @@
updatepasswd() {
if (this.form.newpassword === this.form.surepassword) {
userinfos(data=>{
if(data.code === 1000){
userinfos(data => {
if (data.code === 1000) {
this.userinfo = data.data;
this.$message.success('密码修改成功');
}else {
this.$message.error('密码修改失败,'+data.msg);
} else {
this.$message.error('密码修改失败,' + data.msg);
}
},{"methods":'PUT','data':{
}, {
"methods": 'PUT', 'data': {
"oldpassword": this.form.oldpassword,
"surepassword": this.form.surepassword
}});
}
});
} else {
this.$message.error('密码不一致');
}

@ -5,14 +5,16 @@
<el-form-item>
<el-row>
<el-col :span="22">
<el-input v-model="userinfo.username" prefix-icon="el-icon-user" placeholder="用户名" disabled></el-input>
<el-input v-model="userinfo.username" prefix-icon="el-icon-user" placeholder="用户名"
disabled></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="18">
<el-input v-model="userinfo.qq" :readonly="editqq !== true" ref="qq" prefix-icon="el-icon-message"
<el-input v-model="userinfo.qq" :readonly="editqq !== true" ref="qq"
prefix-icon="el-icon-message"
placeholder="QQ"></el-input>
</el-col>
<el-col :span="2">
@ -87,6 +89,7 @@
<script>
import {userinfos} from '../restful'
export default {
name: "FirUserProfileInfo",
data() {
@ -99,38 +102,38 @@
editposition: false
}
}, methods: {
saveQQ(){
this.updateUserInfo({"methods":'PUT','data':this.userinfo});
saveQQ() {
this.updateUserInfo({"methods": 'PUT', 'data': this.userinfo});
this.changeQQValue()
},
savePositionValue(){
this.updateUserInfo({"methods":'PUT','data':this.userinfo});
savePositionValue() {
this.updateUserInfo({"methods": 'PUT', 'data': this.userinfo});
this.changePositionValue()
},
savePhone(){
this.updateUserInfo({"methods":'PUT','data':this.userinfo});
savePhone() {
this.updateUserInfo({"methods": 'PUT', 'data': this.userinfo});
this.changePhoneValue()
},
updateUserInfo(datainfo){
userinfos(data=>{
if(data.code === 1000){
updateUserInfo(datainfo) {
userinfos(data => {
if (data.code === 1000) {
this.userinfo = data.data;
this.$store.dispatch("getUser",data.data);
if(data.data.sms_code){
this.$store.dispatch("getUser", data.data);
if (data.data.sms_code) {
this.$notify({
title: '验证码',
message: '您正在修改手机号码,验证码为:'+data.data.sms_code,
message: '您正在修改手机号码,验证码为:' + data.data.sms_code,
type: 'success'
});
}
if(datainfo.data){
if (datainfo.data) {
this.$message.success("更新成功")
}
}else {
} else {
this.$message.error("更新失败")
}
},datainfo)
}, datainfo)
},
changePhoneValue() {
this.editphone = !this.editphone;
@ -159,7 +162,7 @@
}
},
getphonecode() {
this.updateUserInfo({"methods":'GET','data':{'act':'sms'}});
this.updateUserInfo({"methods": 'GET', 'data': {'act': 'sms'}});
}
}, mounted() {
@ -167,8 +170,8 @@
// this.updateUserInfo({"methods":false});
this.userinfo = this.$store.state.userinfo;
},watch:{
'$store.state.userinfo':function () {
}, watch: {
'$store.state.userinfo': function () {
this.userinfo = this.$store.state.userinfo;
}
}

@ -1,7 +1,8 @@
<template>
<div style="text-align:center">
<h2 v-if="is_admin_storage">管理员存储您配置的存储将决定其他用户配置的默认存储请谨慎修改</h2>
<el-dialog :title="title" :visible.sync="dialogstorageVisible" :destroy-on-close="true" :close-on-click-modal="false">
<el-dialog :title="title" :visible.sync="dialogstorageVisible" :destroy-on-close="true"
:close-on-click-modal="false">
<el-form v-if="editstorageinfo.id !==-1" ref="storageinfoform" :model="editstorageinfo"
label-width="80px" style="margin:0 auto;">
@ -38,7 +39,8 @@
</el-form-item>
<el-form-item label-width="110px" label="endpoint">
<el-input :disabled='disabled' v-model="editstorageinfo.additionalparameter.endpoint"></el-input>
<el-input :disabled='disabled'
v-model="editstorageinfo.additionalparameter.endpoint"></el-input>
</el-form-item>
</div>
@ -71,20 +73,23 @@
</el-option>
</el-option-group>
</el-select>
<el-button style="margin-left: 10px" round type="info" icon="el-icon-thumb" @click="change_storage_info">保存</el-button>
<el-button style="margin-left: 10px" round type="info" icon="el-icon-thumb"
@click="change_storage_info">保存
</el-button>
<!-- <el-button-group style="margin-left: 10px">-->
<!-- <el-button round type="info" icon="el-icon-edit" @click="change_storage_info"></el-button>-->
<!-- <el-button round type="info" icon="el-icon-plus" @click="add_storage_info"></el-button>-->
<!-- <el-button round type="info" icon="el-icon-delete" @click="del_storage_info"></el-button>-->
<!-- </el-button-group>-->
<!-- <el-button round type="info" icon="el-icon-edit" @click="change_storage_info"></el-button>-->
<!-- <el-button round type="info" icon="el-icon-plus" @click="add_storage_info"></el-button>-->
<!-- <el-button round type="info" icon="el-icon-delete" @click="del_storage_info"></el-button>-->
<!-- </el-button-group>-->
<el-divider></el-divider>
<el-form v-if="storageinfo.id && storageinfo.id !==-1" ref="storageinfoform" :model="storageinfo"
label-width="80px" style="width: 39%;margin:0 auto;">
<el-form-item label-width="110px" label="存储类型">
<el-select :disabled='Sdisabled' v-model="storageinfo.storage_type" placeholder="存储类型"
style="margin-left: -60px">
<el-option v-for="st in storage_list" :key="st.id" :label="st.name" :value="st.id"></el-option>
<el-option v-for="st in storage_list" :key="st.id" :label="st.name"
:value="st.id"></el-option>
</el-select>
</el-form-item>
@ -115,7 +120,8 @@
</el-form-item>
<el-form-item label-width="110px" label="endpoint">
<el-input :disabled='Sdisabled' v-model="storageinfo.additionalparameter.endpoint"></el-input>
<el-input :disabled='Sdisabled'
v-model="storageinfo.additionalparameter.endpoint"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="备注">
@ -173,9 +179,11 @@
fixed="right"
label="操作"
width="120">
<template slot-scope="scope" >
<template slot-scope="scope">
<div v-if="scope.row.id === org_storage_id">
<el-button v-if="scope.row.id === org_storage_id" @click="showstorage(scope.row)" type="success" size="small">使用中</el-button>
<el-button v-if="scope.row.id === org_storage_id" @click="showstorage(scope.row)"
type="success" size="small">使用中
</el-button>
</div>
<div v-else>
<el-button @click="showstorage(scope.row)" type="text" size="small">查看</el-button>
@ -187,7 +195,7 @@
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="新增存储" name="addstorage" >
<el-tab-pane label="新增存储" name="addstorage">
</el-tab-pane>
</el-tabs>
@ -204,12 +212,12 @@
data() {
return {
fstorage_lists: [],
Sdisabled:true,
Sdisabled: true,
use_storage_id: 0,
org_storage_id:0,
title:'',
dialogstorageVisible:false,
editstorageinfo:{'additionalparameter':{}},
org_storage_id: 0,
title: '',
dialogstorageVisible: false,
editstorageinfo: {'additionalparameter': {}},
selectlabel: "",
storageinfo: {'additionalparameter': {}},
storage_list: [],
@ -217,32 +225,32 @@
isaddflag: false,
activeName: 'chostorage',
storage_info_lists: [],
is_admin_storage:false,
is_admin_storage: false,
}
}, methods: {
showstorage(editstorageinfo){
this.title='查看存储信息';
this.disabled=true;
this.dialogstorageVisible=true;
this.editstorageinfo=deepCopy(editstorageinfo);
showstorage(editstorageinfo) {
this.title = '查看存储信息';
this.disabled = true;
this.dialogstorageVisible = true;
this.editstorageinfo = deepCopy(editstorageinfo);
},
editstorage(editstorageinfo){
this.title='存储编辑';
this.disabled=false;
this.dialogstorageVisible=true;
this.editstorageinfo=deepCopy(editstorageinfo);
this.isaddflag=false;
},add_storage_click(){
this.title='新增存储';
this.disabled=false;
this.dialogstorageVisible=true;
this.editstorageinfo={'additionalparameter':{}};
this.isaddflag=true;
editstorage(editstorageinfo) {
this.title = '存储编辑';
this.disabled = false;
this.dialogstorageVisible = true;
this.editstorageinfo = deepCopy(editstorageinfo);
this.isaddflag = false;
}, add_storage_click() {
this.title = '新增存储';
this.disabled = false;
this.dialogstorageVisible = true;
this.editstorageinfo = {'additionalparameter': {}};
this.isaddflag = true;
},
// eslint-disable-next-line no-unused-vars
handleClick(tab, event) {
if(tab.name === 'addstorage'){
if (tab.name === 'addstorage') {
this.add_storage_click()
}
},
@ -254,9 +262,9 @@
getStorageinfo(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
this.dialogstorageVisible=false;
this.dialogstorageVisible = false;
if (this.isaddflag) {
this.activeName='setstorage';
this.activeName = 'setstorage';
}
this.getstorageinfoFun();
@ -295,7 +303,7 @@
this.$message.success('修改成功');
this.getstorageinfoFun();
}
}, {"methods": 'PUT', 'data': {'use_storage_id':this.use_storage_id}});
}, {"methods": 'PUT', 'data': {'use_storage_id': this.use_storage_id}});
this.getstorageinfoFun();
}).catch(() => {
this.$message({
@ -357,13 +365,13 @@
getstorageinfoFun() {
getStorageinfo(data => {
if (data.code === 1000) {
this.org_storage_id=this.use_storage_id = data.storage;
this.org_storage_id = this.use_storage_id = data.storage;
this.storage_list = data.storage_list;
this.storage_info_lists = data.data;
this.is_admin_storage = data.is_admin_storage;
this.format_storage(data.data);
this.disabled=true;
this.isaddflag=false;
this.disabled = true;
this.isaddflag = false;
} else {
this.$message.error('存储获取失败,' + data);
}

@ -4,6 +4,7 @@ import router from "@/router";
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import Vuex from 'vuex'
const qiniu = require('qiniu-js');
Vue.prototype.qiniu = qiniu;

@ -1,6 +1,7 @@
import Axios from 'axios'
import VueCookies from 'vue-cookies'
import router from "../router";
const https = require('https');
const Base64 = require('js-base64').Base64;
Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
@ -11,8 +12,10 @@ Axios.defaults.httpsAgent = new https.Agent({
const DOMAIN = 'https://fly.harmonygames.cn';
const APIPATH='/api/v1/fir/server';
let USERSEVER = DOMAIN+APIPATH;
// const DOMAIN = 'https://testapp.harmonygames.cn';
// const DOMAIN = 'http://172.16.133.34:8000';
const APIPATH = '/api/v1/fir/server';
let USERSEVER = DOMAIN + APIPATH;
export function set_auth_token() {
@ -41,20 +44,7 @@ export function set_auth_token() {
set_auth_token();
function getData(methods = true, url, params = {}, callBack, load, isCode = false) {
if (methods === "DELETE") {
Axios
.delete(url, {params:params})
.then(function (response) {
if (isCode) {
callBack(response.data);
} else {
callBack(response.data.data);
}
})
.catch(function (error) {
function ErrorMsg(error) {
if (error && error.response) {
switch (error.response.status) {
case 400:
@ -96,16 +86,35 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
} else {
error.message = '连接服务器失败!';
}
if (error.response && error.response.status === 403) {
router.push({name: 'FirLogin'});
}
if (error.message === 'Network Error') {
alert('网络连接失败');
} else {
alert(error)
}
}
function getData(methods = true, url, params = {}, callBack, load, isCode = false) {
if (methods === "DELETE") {
Axios
.delete(url, {params: params})
.then(function (response) {
if (isCode) {
callBack(response.data);
} else {
callBack(response.data.data);
}
})
.catch(function (error) {
ErrorMsg(error);
callBack(null);
});
}
else if (methods === "PUT") {
} else if (methods === "PUT") {
Axios
.put(url, params)
.then(function (response) {
@ -116,16 +125,11 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
}
})
.catch(function (error) {
// eslint-disable-next-line no-console
console.log(error, error.response);
ErrorMsg(error);
callBack(null);
});
}
else if (methods === 'POST') {
} else if (methods === 'POST') {
Axios
.post(url, params)
.then(function (response) {
@ -137,8 +141,7 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
})
.catch(function (error) {
// eslint-disable-next-line no-console
console.log(error, error.response);
ErrorMsg(error);
callBack(null);
});
} else {
@ -148,11 +151,8 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
callBack(response.data);
})
.catch(function (error) {
// eslint-disable-next-line no-console
console.log(error, error.response);
if(error.response && error.response.status === 403){
router.push({name: 'FirLogin'});
}
ErrorMsg(error);
callBack(null);
});
}
@ -304,7 +304,7 @@ export function getapptimeline(callBack, params, load = true) {
export function getShortAppinfo(callBack, params, load = true) {
getData(
'GET',
USERSEVER + '/short/' + params.short ,
USERSEVER + '/short/' + params.short,
params,
data => {
callBack(data);
@ -319,7 +319,7 @@ export function getShortAppinfo(callBack, params, load = true) {
export function getStorageinfo(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/storage' ,
USERSEVER + '/storage',
params.data,
data => {
callBack(data);
@ -347,8 +347,9 @@ export function userinfos(callBack, params, load = true) {
}
export function getuserpicurl() {
return USERSEVER+ '/userinfo'
return USERSEVER + '/userinfo'
}
export function getapppicurl(app_id) {
return USERSEVER + '/apps/' + app_id
}
@ -362,7 +363,7 @@ export function getuploadurl() {
export function analyseApps(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/analyse' ,
USERSEVER + '/analyse',
params.data,
data => {
callBack(data);
@ -378,7 +379,7 @@ export function analyseApps(callBack, params, load = true) {
export function getdownloadurl(callBack, params, load = true) {
getData(
'GET',
USERSEVER + '/install/'+params.app_id ,
USERSEVER + '/install/' + params.app_id,
params.data,
data => {
callBack(data);
@ -390,22 +391,22 @@ export function getdownloadurl(callBack, params, load = true) {
}
/**上传文件到服务器 */
export function uploadstorage(certinfo,file,successCallback,processCallback) {
export function uploadstorage(certinfo, file, successCallback, processCallback) {
let config = {
onUploadProgress: function (progressEvent) {
let total=progressEvent.total;
let total = progressEvent.total;
let loaded = progressEvent.loaded;
processCallback(Math.round(loaded*100/total))
processCallback(Math.round(loaded * 100 / total))
},
headers: {
'Content-Type': 'multipart/form-data',
'Authorization':VueCookies.get('auth_token'),
'Authorization': VueCookies.get('auth_token'),
},
};
const data = new FormData();
data.append('file', file);
data.append('certinfo',JSON.stringify(certinfo));
data.append('certinfo', JSON.stringify(certinfo));
Axios.post(certinfo.upload_url, data, config).then(res => {
// eslint-disable-next-line no-console
console.log(res);

@ -31,7 +31,7 @@ import FirUserProfileStorage from "@/components/FirUserProfileStorage";
import FirSuperSignBase from "@/components/FirSuperSignBase";
const router = new VueRouter({
mode:'history',
mode: 'history',
routes: [
{
path: '/',

@ -1,11 +1,11 @@
//滚动条在Y轴上的滚动距离
export function getScrollTop(){
export function getScrollTop() {
let scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;
if(document.body){
if (document.body) {
bodyScrollTop = document.body.scrollTop;
}
if(document.documentElement){
if (document.documentElement) {
documentScrollTop = document.documentElement.scrollTop;
}
scrollTop = (bodyScrollTop - documentScrollTop > 0) ? bodyScrollTop : documentScrollTop;
@ -14,14 +14,13 @@ export function getScrollTop(){
}
//文档的总高度
export function getScrollHeight(){
export function getScrollHeight() {
let scrollHeight = 0, bodyScrollHeight = 0, documentScrollHeight = 0;
if(document.body){
if (document.body) {
bodyScrollHeight = document.body.scrollHeight;
}
if(document.documentElement){
if (document.documentElement) {
documentScrollHeight = document.documentElement.scrollHeight;
}
scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight;
@ -30,41 +29,41 @@ export function getScrollHeight(){
//浏览器视口的高度
export function getWindowHeight(){
export function getWindowHeight() {
let windowHeight = 0;
if(document.compatMode === "CSS1Compat"){
if (document.compatMode === "CSS1Compat") {
windowHeight = document.documentElement.clientHeight;
}else{
} else {
windowHeight = document.body.clientHeight;
}
return windowHeight;
}
export function getappinfo(file,successcallback,errcallback) {
export function getappinfo(file, successcallback, errcallback) {
const AppInfoParser = require('app-info-parser');
const parser = new AppInfoParser(file);
let analyseappinfo={};
let analyseappinfo = {};
parser.parse().then(result => {
analyseappinfo.icon = result.icon;
analyseappinfo.filename = file.name;
analyseappinfo.filesize = file.size;
if(result.CFBundleDisplayName){
if (result.CFBundleDisplayName) {
analyseappinfo.appname = result.CFBundleDisplayName;
analyseappinfo.bundleid = result.CFBundleIdentifier;
analyseappinfo.version = result.CFBundleShortVersionString;
analyseappinfo.buildversion = result.CFBundleVersion;
analyseappinfo.miniosversion = result.MinimumOSVersion;
if(result.mobileProvision.ProvisionedDevices){
analyseappinfo.release_type='Adhoc';
analyseappinfo.release_type_id=1;
analyseappinfo.udid=result.mobileProvision.ProvisionedDevices;
}else {
analyseappinfo.release_type='Inhouse';
analyseappinfo.release_type_id=2;
analyseappinfo.udid=[];
if (result.mobileProvision.ProvisionedDevices) {
analyseappinfo.release_type = 'Adhoc';
analyseappinfo.release_type_id = 1;
analyseappinfo.udid = result.mobileProvision.ProvisionedDevices;
} else {
analyseappinfo.release_type = 'Inhouse';
analyseappinfo.release_type_id = 2;
analyseappinfo.udid = [];
}
analyseappinfo.type = 'iOS';
}else {
} else {
analyseappinfo.appname = result.application.label[0];
analyseappinfo.bundleid = result.package;
analyseappinfo.version = result.versionName;
@ -72,7 +71,7 @@ export function getappinfo(file,successcallback,errcallback) {
analyseappinfo.miniosversion = result.usesSdk.minSdkVersion;
analyseappinfo.type = 'Android';
analyseappinfo.release_type = 'Android';
analyseappinfo.release_type_id=0;
analyseappinfo.release_type_id = 0;
}
successcallback(analyseappinfo)
@ -82,23 +81,23 @@ export function getappinfo(file,successcallback,errcallback) {
});
}
export function uploadqiniuoss(file,certinfo,app,successcallback,processcallback){
export function uploadqiniuoss(file, certinfo, app, successcallback, processcallback) {
// let app = this;
// let file=this.currentfile;
let observer = {
next(res){
next(res) {
processcallback(Math.round(res.total.percent));
},
error(err){
error(err) {
// eslint-disable-next-line no-console
console.log(err);
app.$message({
message:file.name + '上传失败,请刷新页面重试',
message: file.name + '上传失败,请刷新页面重试',
type: 'error',
duration:0
duration: 0
});
},
complete(res){
complete(res) {
// eslint-disable-next-line no-console
// console.log(res);
successcallback(res);
@ -109,7 +108,7 @@ export function uploadqiniuoss(file,certinfo,app,successcallback,processcallback
params: {},
// mimeType: ["image/png"]
};
let config={};
let config = {};
let observable = app.qiniu.upload(file, certinfo.upload_key, certinfo.upload_token, putExtra, config);
// eslint-disable-next-line no-unused-vars
let subscription = observable.subscribe(observer) // 上传开始
@ -119,13 +118,13 @@ export function uploadqiniuoss(file,certinfo,app,successcallback,processcallback
export function dataURLtoFile(dataurl, filename) {//将base64转换为文件
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type:mime});
return new File([u8arr], filename, {type: mime});
}
export function uploadaliyunoss(file,certinfo,app,successcallback,processcallback) {
export function uploadaliyunoss(file, certinfo, app, successcallback, processcallback) {
const OSS = require('ali-oss');
let token = certinfo.upload_token;
let client = new OSS({
@ -155,7 +154,7 @@ export function uploadaliyunoss(file,certinfo,app,successcallback,processcallbac
// people: 'test',
// },
};
client.multipartUpload(certinfo.upload_key, file, options).then( (res) => {
client.multipartUpload(certinfo.upload_key, file, options).then((res) => {
// eslint-disable-next-line no-console
console.log('upload success: %j', res);
successcallback(res);
@ -164,17 +163,18 @@ export function uploadaliyunoss(file,certinfo,app,successcallback,processcallbac
// eslint-disable-next-line no-console
console.error(err);
app.$message({
message:file.name + '上传失败,请刷新页面重试',
message: file.name + '上传失败,请刷新页面重试',
type: 'error',
duration:0
duration: 0
});
});
}
import {uploadstorage} from '../restful'
export function uploadlocalstorage(file,certinfo,app,successcallback,processcallback) {
uploadstorage(certinfo,file,successcallback,processcallback)
export function uploadlocalstorage(file, certinfo, app, successcallback, processcallback) {
uploadstorage(certinfo, file, successcallback, processcallback)
}
@ -185,12 +185,10 @@ export function removeAaary(_arr, _obj) {
if (i == 0) {
_arr.shift(); //删除并返回数组的第一个元素
return _arr;
}
else if (i == length - 1) {
} else if (i == length - 1) {
_arr.pop(); //删除并返回数组的最后一个元素
return _arr;
}
else {
} else {
_arr.splice(i, 1); //删除下标为i的元素
return _arr;
}
@ -200,19 +198,19 @@ export function removeAaary(_arr, _obj) {
//深拷贝
export function deepCopy(source) {
let result={};
let result = {};
for (let key in source) {
result[key] = typeof source[key]==='object'? deepCopy(source[key]): source[key];
result[key] = typeof source[key] === 'object' ? deepCopy(source[key]) : source[key];
}
return result;
}
export function IsNum(s){
if(s!=null){
var r,re;
export function IsNum(s) {
if (s != null) {
var r, re;
re = /\d*/i; //\d表示数字,*表示匹配多个数字
r = s.match(re);
return (r==s)?true:false;
return (r == s) ? true : false;
}
return false;
}

@ -3,7 +3,6 @@ from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(UserInfo)
admin.site.register(Apps)
admin.site.register(AppReleaseInfo)
@ -15,4 +14,3 @@ admin.site.register(AppIOSDeveloperInfo)
admin.site.register(APPSuperSignUsedInfo)
admin.site.register(APPToDeveloper)
admin.site.register(UDIDsyncDeveloper)

@ -7,7 +7,6 @@ import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
@ -21,28 +20,39 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('is_superuser', models.BooleanField(default=False,
help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name='superuser status')),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_staff', models.BooleanField(default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status')),
('username', models.CharField(max_length=64, unique=True, verbose_name='用户名')),
('email', models.EmailField(blank=True, max_length=255, null=True, unique=True, verbose_name='email address')),
('email',
models.EmailField(blank=True, max_length=255, null=True, unique=True, verbose_name='email address')),
('uid', models.CharField(db_index=True, max_length=64, unique=True)),
('mobile', models.BigIntegerField(help_text='用于手机验证码登录', null=True, unique=True, verbose_name='手机')),
('qq', models.BigIntegerField(blank=True, db_index=True, null=True, verbose_name='QQ')),
('is_active', models.BooleanField(default=True, verbose_name='账户状态,默认启用')),
('job', models.TextField(blank=True, max_length=128, null=True, verbose_name='职位')),
('company', models.CharField(blank=True, max_length=128, null=True, verbose_name='公司')),
('gender', models.SmallIntegerField(choices=[(0, '保密'), (1, ''), (2, '')], default=0, verbose_name='性别')),
('gender',
models.SmallIntegerField(choices=[(0, '保密'), (1, ''), (2, '')], default=0, verbose_name='性别')),
('head_img', models.CharField(default='head_img.jpeg', max_length=256, verbose_name='个人头像')),
('role', models.SmallIntegerField(choices=[(0, '普通会员'), (1, 'VIP'), (2, 'SVIP'), (3, '管理员')], default=0, verbose_name='角色')),
('role', models.SmallIntegerField(choices=[(0, '普通会员'), (1, 'VIP'), (2, 'SVIP'), (3, '管理员')], default=0,
verbose_name='角色')),
('memo', models.TextField(blank=True, default=None, null=True, verbose_name='备注')),
('date_joined', models.DateTimeField(auto_now_add=True, verbose_name='注册时间')),
('download_times', models.IntegerField(default=100, verbose_name='下载次数')),
('all_download_times', models.BigIntegerField(default=0, verbose_name='总共下载次数')),
('domain_name', models.CharField(blank=True, max_length=64, null=True, verbose_name='域名')),
('history_release_limit', models.IntegerField(blank=True, default=10, null=True, verbose_name='app 历史记录版本')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('history_release_limit',
models.IntegerField(blank=True, default=10, null=True, verbose_name='app 历史记录版本')),
('groups', models.ManyToManyField(blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
related_name='user_set', related_query_name='user', to='auth.Group',
verbose_name='groups')),
],
options={
'verbose_name': '账户信息',
@ -57,7 +67,8 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64, unique=True, verbose_name='开发者标识')),
('email', models.EmailField(blank=True, max_length=64, null=True, unique=True, verbose_name='email address')),
('email',
models.EmailField(blank=True, max_length=64, null=True, unique=True, verbose_name='email address')),
('password', models.CharField(max_length=64)),
('is_actived', models.BooleanField(default=False, verbose_name='是否已经激活')),
('certid', models.CharField(blank=True, max_length=64, null=True, verbose_name='超级签名自动创建证书ID')),
@ -66,7 +77,8 @@ class Migration(migrations.Migration):
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('description', models.TextField(blank=True, default='', null=True, verbose_name='备注')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户ID')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL,
verbose_name='用户ID')),
],
options={
'verbose_name': '苹果开发者账户',
@ -89,8 +101,10 @@ class Migration(migrations.Migration):
('issupersign', models.BigIntegerField(default=False, verbose_name='是否超级签名包')),
('description', models.TextField(blank=True, default=None, null=True, verbose_name='描述')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('has_combo', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='combo_app_info', to='api.Apps', verbose_name='关联应用')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户ID')),
('has_combo', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL,
related_name='combo_app_info', to='api.Apps', verbose_name='关联应用')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL,
verbose_name='用户ID')),
],
options={
'verbose_name': '应用信息',
@ -106,7 +120,8 @@ class Migration(migrations.Migration):
('address', models.CharField(blank=True, max_length=128, null=True, verbose_name='联系地址')),
('mobile', models.BigIntegerField(blank=True, null=True, verbose_name='联系电话')),
('date_verify', models.DateTimeField(auto_now_add=True, verbose_name='认证时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='verify_info', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='verify_info',
to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '实名认证',
@ -119,7 +134,8 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('access_token', models.CharField(max_length=64, unique=True)),
('created', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auth_token', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auth_token',
to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
),
migrations.CreateModel(
@ -136,7 +152,8 @@ class Migration(migrations.Migration):
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('is_signed', models.BooleanField(default=False, verbose_name='是否完成签名打包')),
('binary_file', models.CharField(blank=True, max_length=128, null=True, verbose_name='签名包名称')),
('app_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
('app_id',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
],
options={
'verbose_name': '设备详情',
@ -150,10 +167,15 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('app_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
('developerid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppIOSDeveloperInfo', verbose_name='所使用苹果开发者账户')),
('udid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppUDID', verbose_name='所消耗的udid')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户ID')),
('app_id',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
('developerid',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppIOSDeveloperInfo',
verbose_name='所使用苹果开发者账户')),
('udid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppUDID',
verbose_name='所消耗的udid')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL,
verbose_name='用户ID')),
],
options={
'verbose_name': '设备使用统计',
@ -165,17 +187,26 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=64, null=True, verbose_name='存储名字')),
('storage_type', models.SmallIntegerField(choices=[(0, '本地存储'), (1, '七牛云存储'), (2, '阿里云存储'), (3, '默认存储')], default=3, verbose_name='存储类型')),
('storage_type',
models.SmallIntegerField(choices=[(0, '本地存储'), (1, '七牛云存储'), (2, '阿里云存储'), (3, '默认存储')], default=3,
verbose_name='存储类型')),
('access_key', models.CharField(blank=True, max_length=128, null=True, verbose_name='存储访问key')),
('secret_key', models.CharField(blank=True, max_length=128, null=True, verbose_name='存储访问secret')),
('bucket_name', models.CharField(blank=True, max_length=128, null=True, verbose_name='存储空间bucket_name')),
('domain_name', models.CharField(blank=True, help_text='fly-storage.dvcloud.xin,可以自定义端口', max_length=128, null=True, verbose_name='下载域名')),
(
'bucket_name',
models.CharField(blank=True, max_length=128, null=True, verbose_name='存储空间bucket_name')),
('domain_name',
models.CharField(blank=True, help_text='fly-storage.dvcloud.xin,可以自定义端口', max_length=128, null=True,
verbose_name='下载域名')),
('is_https', models.BooleanField(default=True, verbose_name='是否支持https')),
('additionalparameters', models.TextField(blank=True, default=None, help_text='阿里云:{"sts_role_arn":"arn信息","endpoint":""} ', null=True, verbose_name='额外参数')),
('additionalparameters',
models.TextField(blank=True, default=None, help_text='阿里云:{"sts_role_arn":"arn信息","endpoint":""} ',
null=True, verbose_name='额外参数')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('description', models.TextField(blank=True, default='', null=True, verbose_name='备注')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户ID')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL,
verbose_name='用户ID')),
],
options={
'verbose_name': '存储配置',
@ -187,10 +218,14 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_master', models.BooleanField(default=True, verbose_name='是否master版本')),
('release_id', models.CharField(db_index=True, max_length=64, unique=True, verbose_name='release 版本id')),
(
'release_id',
models.CharField(db_index=True, max_length=64, unique=True, verbose_name='release 版本id')),
('build_version', models.CharField(blank=True, max_length=16, verbose_name='build版本')),
('app_version', models.CharField(blank=True, max_length=16, verbose_name='app版本')),
('release_type', models.SmallIntegerField(choices=[(0, 'android'), (1, 'adhoc'), (2, 'Inhouse'), (3, 'unknown')], default=0, verbose_name='版本类型')),
('release_type',
models.SmallIntegerField(choices=[(0, 'android'), (1, 'adhoc'), (2, 'Inhouse'), (3, 'unknown')],
default=0, verbose_name='版本类型')),
('minimum_os_version', models.CharField(max_length=64, verbose_name='应用可安装的最低系统版本')),
('binary_size', models.BigIntegerField(verbose_name='应用大小')),
('binary_url', models.CharField(blank=True, max_length=128, verbose_name='第三方下载URL')),
@ -198,7 +233,8 @@ class Migration(migrations.Migration):
('changelog', models.TextField(blank=True, default=None, null=True, verbose_name='更新日志')),
('udid', models.TextField(blank=True, default='', null=True, verbose_name='ios内测版 udid')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('app_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
('app_id',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
],
options={
'verbose_name': '应用详情',
@ -208,12 +244,15 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='userinfo',
name='storage',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='app_storage', to='api.AppStorage', verbose_name='存储'),
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL,
related_name='app_storage', to='api.AppStorage', verbose_name='存储'),
),
migrations.AddField(
model_name='userinfo',
name='user_permissions',
field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.',
related_name='user_set', related_query_name='user', to='auth.Permission',
verbose_name='user permissions'),
),
migrations.AddIndex(
model_name='apps',

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0001_initial'),
]

@ -5,7 +5,6 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('api', '0002_auto_20200503_1120'),
]
@ -17,8 +16,11 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('binary_file', models.CharField(blank=True, max_length=128, null=True, verbose_name='签名包名称')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('app_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
('developerid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppIOSDeveloperInfo', verbose_name='所使用苹果开发者账户')),
('app_id',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
('developerid',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppIOSDeveloperInfo',
verbose_name='所使用苹果开发者账户')),
],
options={
'verbose_name': '设备使用统计',

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0003_apptodeveloper'),
]

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0004_auto_20200504_2054'),
]

@ -5,7 +5,6 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('api', '0005_apptodeveloper_release_file'),
]
@ -20,7 +19,9 @@ class Migration(migrations.Migration):
('serial', models.CharField(blank=True, max_length=64, null=True, verbose_name='序列号')),
('version', models.CharField(blank=True, max_length=64, null=True, verbose_name='型号')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('developerid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppIOSDeveloperInfo', verbose_name='所使用苹果开发者账户')),
('developerid',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppIOSDeveloperInfo',
verbose_name='所使用苹果开发者账户')),
],
options={
'verbose_name': 'iOS开发平台同步设备信息',

@ -14,34 +14,33 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,re_path,include
from django.urls import path, re_path, include
from api.views.login import LoginView,UserInfoView
from api.views.login import LoginView, UserInfoView
from api.views.logout import LogoutView
from api.views.captcha import CaptchaView
from api.views.apps import AppsView,AppInfoView,AppReleaseinfoView
from api.views.apps import AppsView, AppInfoView, AppReleaseinfoView
from api.views.download import ShortDownloadView
from api.views.uploads import AppAnalyseView,UploadView
from api.views.uploads import AppAnalyseView, UploadView
from api.views.storage import StorageView
from api.views.receiveudids import IosUDIDView
from api.views.supersign import DeveloperView,SuperSignUsedView,AppUDIDUsedView
from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView
# router=DefaultRouter()
# router.register("apps", AppsView)
urlpatterns = [
# path("",include(router.urls)),
re_path("^login",LoginView.as_view()),
re_path("^logout",LogoutView.as_view()),
re_path("^captcha_check/",CaptchaView.as_view()),
re_path("^login", LoginView.as_view()),
re_path("^logout", LogoutView.as_view()),
re_path("^captcha_check/", CaptchaView.as_view()),
re_path("^apps$", AppsView.as_view()),
re_path("^storage$", StorageView.as_view()),
re_path("^apps/(?P<app_id>\w+)", AppInfoView.as_view()),
re_path("^appinfos/(?P<app_id>\w+)/(?P<act>\w+)", AppReleaseinfoView.as_view()),
re_path("^upload$",UploadView.as_view()),
re_path("^userinfo",UserInfoView.as_view()),
re_path("^upload$", UploadView.as_view()),
re_path("^userinfo", UserInfoView.as_view()),
re_path("^short/(?P<short>\w+)$", ShortDownloadView.as_view()),
re_path("^udid/(?P<short>\w+)$", IosUDIDView.as_view()),
re_path("^analyse$", AppAnalyseView.as_view()),

@ -16,10 +16,11 @@ from fir_ser.settings import CACHE_KEY_TEMPLATE
cache.set(token_obj.key, token_obj.user, min(delta.total_seconds(), 3600 * 24 * 7))
'''
class DownloadToken(object):
def make_token(self,release_id,time_limit=60,key='',force_new=False):
token_key = "_".join([key.lower(), CACHE_KEY_TEMPLATE.get("make_token_key"),release_id])
def make_token(self, release_id, time_limit=60, key='', force_new=False):
token_key = "_".join([key.lower(), CACHE_KEY_TEMPLATE.get("make_token_key"), release_id])
make_token_key = cache.get(token_key)
if make_token_key and not force_new:
return make_token_key
@ -29,16 +30,16 @@ class DownloadToken(object):
user_ran_str.extend(random_str)
new_str = "".join(user_ran_str)
cache.set(new_str, {
"atime":time.time()+time_limit,
"data":release_id
"atime": time.time() + time_limit,
"data": release_id
}, time_limit)
cache.set(token_key,new_str,time_limit-1)
cache.set(token_key, new_str, time_limit - 1)
return new_str
def verify_token(self,token,release_id):
def verify_token(self, token, release_id):
try:
values = cache.get(token)
if values and release_id in values.get("data",None):
if values and release_id in values.get("data", None):
return True
except Exception as e:
print(e)
@ -55,8 +56,11 @@ def generateTokenForMedium(medium):
else:
return generateNumericTokenOfLength(6)
def generateNumericTokenOfLength(length):
return "".join([random.choice(string.digits) for _ in range(length)])
def generateAlphanumericTokenOfLength(length):
return "".join([random.choice(string.digits + string.ascii_lowercase + string.ascii_uppercase) for _ in range(length)])
return "".join(
[random.choice(string.digits + string.ascii_lowercase + string.ascii_uppercase) for _ in range(length)])

@ -20,7 +20,6 @@ logger = logging.getLogger(__name__)
class BaseAli(object):
API_BASE_URL = ""
_http = requests.session()

@ -16,7 +16,6 @@ from .yun.basic import AliYunClient
class AliApi(object):
# 默认的支付配置
DEFAULT_ALI_PAY_CONFIG = settings.THIRD_PART_CONFIG["ALI_PAY"]["default"]
# 指定的支付配置(业务拓展)

@ -1,4 +1,3 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2

@ -1,4 +1,3 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2

@ -1,4 +1,3 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2

@ -1,4 +1,3 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2

@ -4,7 +4,6 @@
class BaseAliPayAPI(object):
"""
支付宝支付API基类

@ -4,7 +4,6 @@
class BaseAliYunAPI(object):
"""
阿里云服务API基类

@ -10,7 +10,6 @@ from .base import BaseAliYunAPI
class AliYunSms(BaseAliYunAPI):
VERSION = "2017-05-25"
API_BASE_URL = "http://dysmsapi.aliyuncs.com/"

@ -10,7 +10,6 @@ from .base import BaseAliYunAPI
class AliYunVod(BaseAliYunAPI):
VERSION = "2017-03-21"
API_BASE_URL = "http://vod.cn-shanghai.aliyuncs.com/"

@ -17,7 +17,6 @@ from ..tools import get_iso_8061_date
logger = logging.getLogger(__name__)
QUERY_STRING = None
@ -66,7 +65,6 @@ def _is_api_endpoint(instance):
class AliYunClient(BaseAli):
VERSION = "" # 每个接口都需要版本号
API_BASE_URL = "" # 阿里云api网关地址均从使用的接口定义

@ -4,13 +4,13 @@
# author: liuyu
# date: 2020/3/6
from api.utils.app.randomstrings import make_app_uuid
from api.models import AppReleaseInfo,Apps
from api.models import AppReleaseInfo, Apps
import random
from api.utils.storage.storage import Storage
from api.utils.storage.caches import del_cache_response_by_short
def make_resigned(bin_url,img_url,bundle_id,app_version,name):
def make_resigned(bin_url, img_url, bundle_id, app_version, name):
ios_plist_tem = """<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
<key>items</key>
@ -59,6 +59,7 @@ def make_resigned(bin_url,img_url,bundle_id,app_version,name):
return ios_plist_tem
def bytes2human(n):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
@ -71,7 +72,7 @@ def bytes2human(n):
return '%sB' % n
def get_release_type(app_file_name,appinfo):
def get_release_type(app_file_name, appinfo):
extension = app_file_name.split(".")[1]
if extension == "ipa":
release_type = appinfo.get("release_type")
@ -84,6 +85,7 @@ def get_release_type(app_file_name,appinfo):
else:
return 0
def get_app_type(app_file_name):
extension = app_file_name.split(".")[1]
if extension == "ipa":
@ -93,21 +95,20 @@ def get_app_type(app_file_name):
else:
return 2
def get_random_short():
def get_random_short():
short_url = ''.join(random.sample(
['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f',
'e', 'd', 'c', 'b', 'a'], 4))
appobj=Apps.objects.filter(short=short_url).first()
appobj = Apps.objects.filter(short=short_url).first()
if appobj:
return get_random_short()
else:
return short_url
def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
app_uuid = make_app_uuid(user_obj,bundle_id+app_file_name.split(".")[1])
def SaveAppInfos(app_file_name, user_obj, appinfo, bundle_id, app_img, short, size):
app_uuid = make_app_uuid(user_obj, bundle_id + app_file_name.split(".")[1])
##判断是否存在该app
appmobj = Apps.objects.filter(app_id=app_uuid, user_id=user_obj).first()
storage = Storage(user_obj)
@ -130,7 +131,7 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
return
else:
try:
del_cache_response_by_short(appmobj.short,appmobj.app_id)
del_cache_response_by_short(appmobj.short, appmobj.app_id)
appmobj.short = short
appmobj.name = appinfo["labelname"]
appmobj.bundle_id = bundle_id
@ -153,8 +154,8 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
"minimum_os_version": appinfo.get("miniOSversion", None),
"binary_size": size,
"is_master": True,
"changelog":appinfo.get("changelog",''),
"udid":appinfo.get("udid",''),
"changelog": appinfo.get("changelog", ''),
"udid": appinfo.get("udid", ''),
}
AppReleaseInfo.objects.create(**release_data)
@ -171,9 +172,8 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
if release_queryset.count() > history_release_limit:
flag = 0
for release_obj in release_queryset:
flag +=1
flag += 1
if flag > history_release_limit:
storage.delete_file(release_obj.release_id,appmobj.type)
storage.delete_file(release_obj.release_id, appmobj.type)
storage.delete_file(release_obj.icon_url)
release_obj.delete()

@ -12,7 +12,7 @@ from androguard.core.bytecodes import apk
import plistlib
def get_android_data( package_file):
def get_android_data(package_file):
try:
apkobj = apk.APK(package_file)
except Exception as err:
@ -26,7 +26,8 @@ def get_android_data( package_file):
sdk_version = apkobj.get_target_sdk_version()
return labelname, bundle_id, versioncode, versioname, sdk_version
def get_ios_data( ios_file):
def get_ios_data(ios_file):
if zipfile.is_zipfile(ios_file):
ipaobj = zipfile.ZipFile(ios_file)
info_path = get_ios_info_path(ipaobj)
@ -39,22 +40,23 @@ def get_ios_data( ios_file):
print("inhouse")
def get_ios_info_path( ipaobj):
def get_ios_info_path(ipaobj):
infopath_re = re.compile(r'.*.app/embedded.mobileprovision')
for i in ipaobj.namelist():
m = infopath_re.match(i)
if m is not None:
return m.group()
def get_ios_icon_path( ipaobj):
def get_ios_icon_path(ipaobj):
infopath_re = re.compile(r'Payload/[^/]*.app/AppIcon[^/]*[^(ipad)].png')
for i in ipaobj.namelist():
m = infopath_re.match(i)
if m is not None:
return m.group()
def get_package_size( package_path):
def get_package_size(package_path):
fsize = os.path.getsize(package_path)
fsize = fsize / float(1024 * 1024)
return round(fsize, 2)

@ -5,7 +5,7 @@
# date: 2019/12/19
import requests
import zipfile, re, os, random,io
import zipfile, re, os, random, io
from androguard.core.bytecodes import apk
import plistlib
import qrcode
@ -24,13 +24,12 @@ def bytes2human(n):
return '%sB' % n
class AppInfo(object):
def __init__(self, app_path):
self.app_path = app_path
self.result = {}
def make_app_png(self,png_path,filename):
def make_app_png(self, png_path, filename):
zf = zipfile.ZipFile(self.app_path)
name_list = zf.namelist()
if self.app_path.endswith("apk"):
@ -59,14 +58,13 @@ class AppInfo(object):
if size == 0:
raise Exception("File size error")
if not os.path.isdir(png_path):
os.makedirs(png_path)
with open(os.path.join(png_path, "%s.png" %filename), 'wb') as f:
with open(os.path.join(png_path, "%s.png" % filename), 'wb') as f:
f.write(zf.read(iconfile))
return "%s.png" %filename
return "%s.png" % filename
def get_app_data(self):
if self.app_path.endswith("apk"):
@ -122,7 +120,7 @@ class AppInfo(object):
"bundle_id": bundle_id,
"versioncode": versioncode,
"version": version,
"miniOSversion":miniOSversion,
"miniOSversion": miniOSversion,
"type": "ios",
}
@ -247,12 +245,12 @@ class FirApi(object):
res = self.__request("post", binary_upload_url, data=binary_data, files=files)
if res.status_code == 200:
print(res.json())
QrCode.make_logo_qr(os.path.join("https://fir.im/", short_url), icon_path, "fir_%s.png"%(appinfo["labelname"]))
QrCode.make_logo_qr(os.path.join("https://fir.im/", short_url), icon_path,
"fir_%s.png" % (appinfo["labelname"]))
else:
raise Exception(res.content)
def get_app_list(self):
url = "%s?api_token=%s" % (self.app_url, self.api_token)
res = self.__request("get", url)
@ -363,23 +361,23 @@ class QrCode(object):
img.save(save_path)
else:
imgByteArr = io.BytesIO()
img.save(imgByteArr,format="PNG")
img.save(imgByteArr, format="PNG")
return imgByteArr.getvalue()
if __name__ == '__main__':
apk_path = "/root/hehemajiang.apk"
API_TOKEN = "3d2a41159a7d8c396ddf0e88799de14a"
fir = FirApi(API_TOKEN)
#上传
# 上传
fir.upload_app(apk_path)
# 获取版本信息
# print(fir.get_version_by_id("5dfb54d3b2eb4612589c3040"))
#获取app详细信息
# 获取app详细信息
# print(fir.get_app_infos_by_id("5dfb54d3b2eb4612589c3040"))
#获取列表
# 获取列表
# print(fir.get_app_list())

@ -4,109 +4,111 @@
# author: liuyu
# date: 2020/4/24
from api.utils.app.shellcmds import shell_command,use_user_pass,pshell_command
from api.utils.app.shellcmds import shell_command, use_user_pass, pshell_command
from fir_ser.settings import SUPER_SIGN_ROOT
import os
from api.utils.app.randomstrings import make_app_uuid
def exec_shell(cmd,remote=False,timeout=None):
def exec_shell(cmd, remote=False, timeout=None):
if remote:
hostip="10.66.6.66"
port=65534
user="root"
passwd="root"
result = use_user_pass(hostip,port,user,passwd,cmd)
hostip = "10.66.6.66"
port = 65534
user = "root"
passwd = "root"
result = use_user_pass(hostip, port, user, passwd, cmd)
print(result)
return result
else:
print(cmd)
result = shell_command(cmd,timeout)
result = shell_command(cmd, timeout)
print(result)
if result.get("exit_code") != 0 :
err_info=result.get("err_info",None)
if result.get("exit_code") != 0:
err_info = result.get("err_info", None)
if err_info:
if "have access to this membership resource. Contact your team" in err_info:
result["err_info"]="You currently don't have access to this membership resource"
result["err_info"] = "You currently don't have access to this membership resource 【您没有权限执行该操作】"
elif "Maximum number of certificates generated" in err_info:
result["err_info"] = "Maximum number of certificates generated 【开发证书数量到上限】"
else:
result["err_info"]="Unknown Error"
return False,result
return True,result
result["err_info"] = "Unknown Error"
return False, result
return True, result
class AppDeveloperApi(object):
def __init__(self,username,password,certid):
def __init__(self, username, password, certid):
self.username = username
self.password = password
self.certid = certid
script_path=os.path.join(SUPER_SIGN_ROOT,'scripts','apple_api.rb')
self.cmd = "ruby %s '%s' '%s'" %(script_path,self.username,self.password)
script_path = os.path.join(SUPER_SIGN_ROOT, 'scripts', 'apple_api.rb')
self.cmd = "ruby %s '%s' '%s'" % (script_path, self.username, self.password)
def active(self,user_obj):
def active(self, user_obj):
self.cmd = self.cmd + " active "
print(self.cmd)
result={}
result = {}
try:
result = pshell_command(self.cmd,user_obj,self.username)
result = pshell_command(self.cmd, user_obj, self.username)
print(result)
if result["exit_code"] == 0:
return True,result
return True, result
except Exception as e:
print(e)
return False,result
return False, result
def file_format_path_name(self,user_obj):
cert_dir_name = make_app_uuid(user_obj,self.username)
cert_dir_path = os.path.join(SUPER_SIGN_ROOT,cert_dir_name)
def file_format_path_name(self, user_obj):
cert_dir_name = make_app_uuid(user_obj, self.username)
cert_dir_path = os.path.join(SUPER_SIGN_ROOT, cert_dir_name)
if not os.path.isdir(cert_dir_path):
os.makedirs(cert_dir_path)
return os.path.join(cert_dir_path,cert_dir_name)
return os.path.join(cert_dir_path, cert_dir_name)
def create_cert(self,user_obj):
self.cmd=self.cmd + " cert add '%s'" %(self.file_format_path_name(user_obj))
def create_cert(self, user_obj):
self.cmd = self.cmd + " cert add '%s'" % (self.file_format_path_name(user_obj))
return exec_shell(self.cmd)
def get_profile(self,bundleId,app_id,device_udid,device_name,provisionName):
self.cmd=self.cmd + " profile add '%s' '%s' '%s' '%s' '%s' '%s'" %(bundleId,app_id,device_udid,device_name,self.certid,provisionName)
def get_profile(self, bundleId, app_id, device_udid, device_name, provisionName):
self.cmd = self.cmd + " profile add '%s' '%s' '%s' '%s' '%s' '%s'" % (
bundleId, app_id, device_udid, device_name, self.certid, provisionName)
result = exec_shell(self.cmd)
def del_profile(self,bundleId,app_id):
self.cmd=self.cmd + " profile del '%s' '%s'" %(bundleId,app_id)
def del_profile(self, bundleId, app_id):
self.cmd = self.cmd + " profile del '%s' '%s'" % (bundleId, app_id)
result = exec_shell(self.cmd)
def set_device_status(self,status,device_udid):
def set_device_status(self, status, device_udid):
if status == "enable":
self.cmd=self.cmd + " device enable '%s'" %(device_udid)
self.cmd = self.cmd + " device enable '%s'" % (device_udid)
else:
self.cmd=self.cmd + " device disable '%s'" %(device_udid)
self.cmd = self.cmd + " device disable '%s'" % (device_udid)
result = exec_shell(self.cmd)
def add_device(self,device_udid,device_name):
self.cmd=self.cmd + " device add '%s' '%s'" %(device_udid,device_name)
def add_device(self, device_udid, device_name):
self.cmd = self.cmd + " device add '%s' '%s'" % (device_udid, device_name)
result = exec_shell(self.cmd)
def get_device(self,user_obj):
self.cmd=self.cmd + " device get '%s' " %(self.file_format_path_name(user_obj))
def get_device(self, user_obj):
self.cmd = self.cmd + " device get '%s' " % (self.file_format_path_name(user_obj))
return exec_shell(self.cmd)
def add_app(self,bundleId,app_id):
self.cmd=self.cmd + " app add '%s' '%s'" %(bundleId,app_id)
def add_app(self, bundleId, app_id):
self.cmd = self.cmd + " app add '%s' '%s'" % (bundleId, app_id)
result = exec_shell(self.cmd)
def del_app(self,bundleId,app_id):
self.cmd=self.cmd + " app del '%s' '%s'" %(bundleId,app_id)
def del_app(self, bundleId, app_id):
self.cmd = self.cmd + " app del '%s' '%s'" % (bundleId, app_id)
result = exec_shell(self.cmd)
class ResignApp(object):
def __init__(self,my_local_key,app_dev_pem):
self.my_local_key=my_local_key
def __init__(self, my_local_key, app_dev_pem):
self.my_local_key = my_local_key
self.app_dev_pem = app_dev_pem
# script_path=os.path.join(SUPER_SIGN_ROOT,'scripts','apple_api.rb')
self.cmd = "isign -c '%s' -k '%s' " %(self.app_dev_pem,self.my_local_key)
def sign(self,new_profile,org_ipa,new_ipa):
self.cmd = "isign -c '%s' -k '%s' " % (self.app_dev_pem, self.my_local_key)
self.cmd = self.cmd + " -p '%s' -o '%s' '%s'" %(new_profile,new_ipa,org_ipa)
def sign(self, new_profile, org_ipa, new_ipa):
self.cmd = self.cmd + " -p '%s' -o '%s' '%s'" % (new_profile, new_ipa, org_ipa)
result = exec_shell(self.cmd)

@ -5,24 +5,23 @@
# date: 2020/3/6
import uuid
def make_from_user_uuid(userinfo):
user_id = userinfo.uid
random_str = uuid.uuid1().__str__().split("-")[0:-1]
user_ran_str=uuid.uuid5(uuid.NAMESPACE_DNS, user_id).__str__().split("-")
user_ran_str = uuid.uuid5(uuid.NAMESPACE_DNS, user_id).__str__().split("-")
user_ran_str.extend(random_str)
new_str = "".join(user_ran_str)
return new_str
def make_app_uuid(userinfo,bundleid):
def make_app_uuid(userinfo, bundleid):
user_id = userinfo.uid
app_uuid=uuid.uuid5(uuid.NAMESPACE_DNS, "%s"%(user_id+bundleid)).__str__().split("-")
app_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, "%s" % (user_id + bundleid)).__str__().split("-")
fapp_uuid = "".join(app_uuid)
return fapp_uuid
def make_random_uuid():
random_str = uuid.uuid1().__str__().split("-")
return "".join(random_str)

@ -13,79 +13,78 @@ Python生成二维码 v1.0
'''
import qrcode
from PIL import Image
import os
#生成二维码图片
def make_qr(str,save):
qr=qrcode.QRCode(
version=4, #生成二维码尺寸的大小 1-40 1:21*21(21+(n-1)*4)
error_correction=qrcode.constants.ERROR_CORRECT_M, #L:7% M:15% Q:25% H:30%
box_size=10, #每个格子的像素大小
border=2, #边框的格子宽度大小
# 生成二维码图片
def make_qr(str, save):
qr = qrcode.QRCode(
version=4, # 生成二维码尺寸的大小 1-40 1:21*21(21+(n-1)*4)
error_correction=qrcode.constants.ERROR_CORRECT_M, # L:7% M:15% Q:25% H:30%
box_size=10, # 每个格子的像素大小
border=2, # 边框的格子宽度大小
)
qr.add_data(str)
qr.make(fit=True)
img=qr.make_image()
img = qr.make_image()
img.save(save)
#生成带logo的二维码图片
def make_logo_qr(str,logo,save):
#参数配置
qr=qrcode.QRCode(
# 生成带logo的二维码图片
def make_logo_qr(str, logo, save):
# 参数配置
qr = qrcode.QRCode(
version=4,
error_correction=qrcode.constants.ERROR_CORRECT_Q,
box_size=8,
border=2
)
#添加转换内容
# 添加转换内容
qr.add_data(str)
#
qr.make(fit=True)
#生成二维码
img=qr.make_image()
# 生成二维码
img = qr.make_image()
#
img=img.convert("RGBA")
img = img.convert("RGBA")
#添加logo
# 添加logo
if logo and os.path.exists(logo):
icon=Image.open(logo)
#获取二维码图片的大小
img_w,img_h=img.size
factor=4
size_w=int(img_w/factor)
size_h=int(img_h/factor)
#logo图片的大小不能超过二维码图片的1/4
icon_w,icon_h=icon.size
if icon_w>size_w:
icon_w=size_w
if icon_h>size_h:
icon_h=size_h
icon=icon.resize((icon_w,icon_h),Image.ANTIALIAS)
#详见:http://pillow.readthedocs.org/handbook/tutorial.html
#计算logo在二维码图中的位置
w=int((img_w-icon_w)/2)
h=int((img_h-icon_h)/2)
icon=icon.convert("RGBA")
img.paste(icon,(w,h),icon)
#详见:http://pillow.readthedocs.org/reference/Image.html#PIL.Image.Image.paste
#保存处理后图片
icon = Image.open(logo)
# 获取二维码图片的大小
img_w, img_h = img.size
factor = 4
size_w = int(img_w / factor)
size_h = int(img_h / factor)
# logo图片的大小不能超过二维码图片的1/4
icon_w, icon_h = icon.size
if icon_w > size_w:
icon_w = size_w
if icon_h > size_h:
icon_h = size_h
icon = icon.resize((icon_w, icon_h), Image.ANTIALIAS)
# 详见:http://pillow.readthedocs.org/handbook/tutorial.html
# 计算logo在二维码图中的位置
w = int((img_w - icon_w) / 2)
h = int((img_h - icon_h) / 2)
icon = icon.convert("RGBA")
img.paste(icon, (w, h), icon)
# 详见:http://pillow.readthedocs.org/reference/Image.html#PIL.Image.Image.paste
# 保存处理后图片
img.save(save)
if __name__=='__main__':
save_path='base__runmethod_02.png' #生成后的保存文件
logo='/tmp/pycharm_project_986/fir/tmp/icon/cmjpwyltznbioseh' #logo图片
str="https://fir.im/luy4"
if __name__ == '__main__':
save_path = 'base__runmethod_02.png' # 生成后的保存文件
logo = '/tmp/pycharm_project_986/fir/tmp/icon/cmjpwyltznbioseh' # logo图片
str = "https://fir.im/luy4"
make_logo_qr(str,logo,save_path)
make_logo_qr(str, logo, save_path)

@ -3,38 +3,37 @@
# project: 3月
# author: liuyu
# date: 2020/3/6
import time,re,os,signal
import time, re, os, signal
from subprocess import Popen, PIPE
import logging
import paramiko,json
import paramiko, json
import socket
import pexpect
from api.utils.storage.caches import developer_auth_code
def default_result():
return {'exit_code': '99', 'return_info': 'Failed to run, function_name is not existed'}
def portisonline(host,port):
result={}
def portisonline(host, port):
result = {}
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.settimeout(3)
result["host"] = host
result["port"]=port
result["functionname"]="CHECK_HOST_ONLINE:"
result["port"] = port
result["functionname"] = "CHECK_HOST_ONLINE:"
try:
sk.connect((host,port))
result["message"]="Connection success"
result["code"]=0
sk.connect((host, port))
result["message"] = "Connection success"
result["code"] = 0
except Exception:
result["message"]="SSH Port Connection refused"
result["code"]=1
result["message"] = "SSH Port Connection refused"
result["code"] = 1
sk.close()
return result
class SSHConnection(object):
def __init__(self, host, port, username, password):
self._host = host
@ -51,23 +50,22 @@ class SSHConnection(object):
transport.connect(username=self._username, password=self._password)
self._transport = transport
#下载
# 下载
def download(self, remotepath, localpath):
if self._sftp is None:
self._sftp = paramiko.SFTPClient.from_transport(self._transport)
self._sftp.get(remotepath, localpath)
self.close()
#上传
# 上传
def put(self, localpath, remotepath):
if self._sftp is None:
self._sftp = paramiko.SFTPClient.from_transport(self._transport)
self.exec_command('mkdir -p %s'%(os.path.dirname(remotepath)))
self.exec_command('mkdir -p %s' % (os.path.dirname(remotepath)))
self._sftp.put(localpath, remotepath)
self.close()
#执行命令
# 执行命令
def exec_command(self, command):
if self._client is None:
self._client = paramiko.SSHClient()
@ -75,10 +73,10 @@ class SSHConnection(object):
stdin, stdout, stderr = self._client.exec_command(command)
data = stdout.read()
if len(data) > 0:
return {'status':0,'infos':data}
return {'status': 0, 'infos': data}
err = stderr.read()
if len(err) > 0:
return {'status':1,'infos':err}
return {'status': 1, 'infos': err}
return {'status': 0, 'infos': data}
def close(self):
@ -87,30 +85,33 @@ class SSHConnection(object):
if self._client:
self._client.close()
def pshell_command(cmdstrs,user_obj,developer_email,timeout=60*10):
def pshell_command(cmdstrs, user_obj, developer_email, timeout=60 * 10):
result = default_result()
run = pexpect.spawn(cmdstrs, encoding='utf-8')
try:
index = run.expect(["Two-factor Authentication","Invalid username and password combination", pexpect.EOF, pexpect.TIMEOUT],timeout=timeout)
index = run.expect(
["Two-factor Authentication", "Invalid username and password combination", pexpect.EOF, pexpect.TIMEOUT],
timeout=timeout)
if index == 0:
count = 1
if timeout == -1:
timeout = 60*60*24
while True and count<timeout/3:
code = developer_auth_code("get",user_obj,developer_email)
timeout = 60 * 60 * 24
while True and count < timeout / 3:
code = developer_auth_code("get", user_obj, developer_email)
if code:
run.sendline(code)
try:
err = run.expect(["Unauthorized Access", pexpect.EOF, pexpect.TIMEOUT])
if err == 0:
result['exit_code'] = 2
result['return_info'] = 'Unauthorized Access'
result['return_info'] = 'Unauthorized Access 【验证码有误】'
elif err == 1:
result['exit_code'] = 0
result['return_info'] = 'Verify Code Success'
else:
result['exit_code'] = 3
result['return_info'] = 'Verify Code TimeOut or UnKnown Error'
result['return_info'] = 'Verify Code TimeOut or UnKnown Error 【双重验证未知错误】'
developer_auth_code("del", user_obj, developer_email)
return result
except Exception as e:
@ -119,23 +120,23 @@ def pshell_command(cmdstrs,user_obj,developer_email,timeout=60*10):
result['exit_code'] = 0
result['return_info'] = 'Success'
return result
count+=1
count += 1
time.sleep(3)
elif index == 1:
result['exit_code'] = 1
result['return_info'] = 'Invalid username and password combination'
result['return_info'] = 'Invalid username and password combination 【密码或者账号有误】'
elif index == 2:
result['exit_code'] = 0
result['return_info'] = 'Alreay Success'
else:
result['exit_code'] = 4
result['return_info'] = 'Code TimeOut or UnKnown Error'
result['return_info'] = 'Code TimeOut or UnKnown Error 【验证超时】'
except Exception as e:
print(e)
return result
def shell_command(cmdstrs,timeout):
def shell_command(cmdstrs, timeout):
result = default_result()
result['return_info'] = ''
shell_start_time = time.time()
@ -147,12 +148,12 @@ def shell_command(cmdstrs,timeout):
if int(now - shell_start_time) > timeout:
os.kill(child.pid, signal.SIGKILL)
os.waitpid(-1, os.WNOHANG)
result['exit_code']=126
result['exit_code'] = 126
return result
out, err = child.communicate()
if err:
result['err_info'] = err
result['err_info'] = err.decode("utf-8")
shell_end_time = time.time()
result['shell_run_time'] = shell_end_time - shell_start_time
out = out.strip(b'\n')
@ -161,7 +162,8 @@ def shell_command(cmdstrs,timeout):
logging.info(u'shell: %s - %s%s - %s%d' % (cmdstrs, 'return_info: ', out, 'exit_code: ', child.returncode))
return result
def use_user_pass(hostip,port,user,passwd,cmdstrs):
def use_user_pass(hostip, port, user, passwd, cmdstrs):
result = default_result()
checkinfo = portisonline(hostip, port)
if checkinfo["code"] != 0:
@ -172,17 +174,15 @@ def use_user_pass(hostip,port,user,passwd,cmdstrs):
conn = SSHConnection(hostip, port, user, passwd)
result['return_info'] = ''
shell_start_time = time.time()
out=conn.exec_command(cmdstrs)
out = conn.exec_command(cmdstrs)
shell_end_time = time.time()
result['shell_run_time'] = shell_end_time - shell_start_time
outs =str(out['infos'],'utf-8')
outs = str(out['infos'], 'utf-8')
outs = outs.strip('\n')
result['return_info'] = outs
result['exit_code'] = out['status']
# logging.info(u'shell: %s - %s%s - %s%d' % (cmdstrs, 'return_info: ', out, 'exit_code: ', out['status']))
print('host: %s user:%s - shell: %s - %s%s - %s%d' % (hostip,user,cmdstrs, 'return_info: ', out, 'exit_code: ', out['status']))
print('host: %s user:%s - shell: %s - %s%s - %s%d' % (
hostip, user, cmdstrs, 'return_info: ', out, 'exit_code: ', out['status']))
conn.close()
return result

@ -7,12 +7,13 @@
import uuid, xmltodict, os, re
from fir_ser.settings import SUPER_SIGN_ROOT, MEDIA_ROOT, SERVER_DOMAIN
from api.utils.app.iossignapi import AppDeveloperApi, ResignApp
from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo,Apps,APPToDeveloper,UDIDsyncDeveloper
from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \
UDIDsyncDeveloper
from api.utils.app.randomstrings import make_app_uuid, make_from_user_uuid
from api.utils.serializer import get_developer_udided
from api.utils.storage.localApi import LocalStorage
from api.utils.storage.caches import del_cache_response_by_short
from api.utils.utils import file_format_path,delete_app_to_dev_and_file,delete_app_profile_file
from api.utils.utils import file_format_path, delete_app_to_dev_and_file, delete_app_profile_file
def udid_bytes_to_dict(xml_stream):
@ -76,7 +77,7 @@ def get_post_udid_url(request, short):
# PATH_INFO_lists.pop(-2)
# PATH_INFO_lists.append(short)
# PATH_INFO_lists.insert(0, server_domain)
PATH_INFO_lists=[server_domain,"udid",short]
PATH_INFO_lists = [server_domain, "udid", short]
udid_url = "/".join(PATH_INFO_lists)
return udid_url
@ -106,7 +107,7 @@ def get_redirect_server_domain(request):
class IosUtils(object):
def __init__(self, udid_info,user_obj,app_obj=None):
def __init__(self, udid_info, user_obj, app_obj=None):
self.udid_info = udid_info
self.app_obj = app_obj
self.user_obj = user_obj
@ -123,22 +124,22 @@ class IosUtils(object):
self.auth = auth
def get_developer_user_by_app_udid(self):
usedeviceobj = APPSuperSignUsedInfo.objects.filter(udid__udid=self.udid_info.get('udid'), #app_id=self.app_obj,
user_id=self.user_obj).first()
#只要账户下面存在udid,就可以使用该苹果开发者账户,避免多个开发者账户下面出现同一个udid
if usedeviceobj :#and usedeviceobj.developerid.use_number < usedeviceobj.developerid.usable_number:
usedeviceobj = APPSuperSignUsedInfo.objects.filter(udid__udid=self.udid_info.get('udid'),
user_id=self.user_obj, developerid__is_actived=True,
developerid__certid__isnull=False).first()
# 只要账户下面存在udid,就可以使用该苹果开发者账户,避免多个开发者账户下面出现同一个udid
if usedeviceobj:
developer_obj = usedeviceobj.developerid
else:
developer_udid_obj = UDIDsyncDeveloper.objects.filter(udid=self.udid_info.get('udid')).first()
developer_udid_obj = UDIDsyncDeveloper.objects.filter(udid=self.udid_info.get('udid'),
developerid__is_actived=True,
developerid__certid__isnull=False).first()
if developer_udid_obj:
developer_obj = developer_udid_obj.developerid
else:
# developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=self.user_obj, is_actived=True,
# use_number__lt=F("usable_number")).order_by(
# "created_time").first()
for developer_obj in AppIOSDeveloperInfo.objects.filter(user_id=self.user_obj,
is_actived=True, ).order_by("created_time"):
is_actived=True, certid__isnull=False).order_by(
"created_time"):
usable_number = developer_obj.usable_number
flyapp_used = get_developer_udided(developer_obj)[1]
if flyapp_used < usable_number:
@ -164,14 +165,14 @@ class IosUtils(object):
def resign(self):
if AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).first().is_signed:
apptodev_obj=APPToDeveloper.objects.filter(app_id=self.app_obj).first()
apptodev_obj = APPToDeveloper.objects.filter(app_id=self.app_obj).first()
if apptodev_obj:
release_obj = AppReleaseInfo.objects.filter(app_id=self.app_obj, is_master=True).first()
if release_obj.release_id == apptodev_obj.release_file:
return
self.download_profile()
file_format_path_name = file_format_path(self.user_obj,self.auth)
file_format_path_name = file_format_path(self.user_obj, self.auth)
my_local_key = file_format_path_name + ".key"
app_dev_pem = file_format_path_name + ".pem"
ResignAppObj = ResignApp(my_local_key, app_dev_pem)
@ -188,43 +189,49 @@ class IosUtils(object):
"is_signed": True,
"binary_file": random_file_name
}
#更新已经完成签名状态,设备消耗记录,和开发者已消耗数量
# 更新已经完成签名状态,设备消耗记录,和开发者已消耗数量
AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).update(**newdata)
appsupersign_obj=APPSuperSignUsedInfo.objects.filter(udid__udid=self.udid_info.get('udid'),developerid=self.developer_obj)
appsupersign_obj = APPSuperSignUsedInfo.objects.filter(udid__udid=self.udid_info.get('udid'),
developerid=self.developer_obj)
if appsupersign_obj.count() == 0:
developer_obj = self.developer_obj
developer_obj.use_number=developer_obj.use_number + 1
developer_obj.use_number = developer_obj.use_number + 1
developer_obj.save()
if not appsupersign_obj.filter(app_id=self.app_obj, user_id=self.user_obj).first():
APPSuperSignUsedInfo.objects.create(app_id=self.app_obj, user_id=self.user_obj, developerid=self.developer_obj,
udid=AppUDID.objects.filter(app_id=self.app_obj,udid=self.udid_info.get('udid')).first())
del_cache_response_by_short(self.app_obj.short,self.app_obj.app_id,udid=self.udid_info.get('udid'))
APPSuperSignUsedInfo.objects.create(app_id=self.app_obj, user_id=self.user_obj,
developerid=self.developer_obj,
udid=AppUDID.objects.filter(app_id=self.app_obj,
udid=self.udid_info.get('udid')).first())
del_cache_response_by_short(self.app_obj.short, self.app_obj.app_id, udid=self.udid_info.get('udid'))
app_udid_obj = UDIDsyncDeveloper.objects.filter(developerid=self.developer_obj, udid=self.udid_info.get('udid'))
if not app_udid_obj:
device=AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).values("serial", 'product', 'udid','version').first()
device = AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).values("serial",
'product',
'udid',
'version').first()
UDIDsyncDeveloper.objects.create(developerid=self.developer_obj, **device)
#创建
apptodev_obj=APPToDeveloper.objects.filter(developerid=self.developer_obj,app_id=self.app_obj).first()
# 创建
apptodev_obj = APPToDeveloper.objects.filter(developerid=self.developer_obj, app_id=self.app_obj).first()
if apptodev_obj:
storage = LocalStorage("localhost", False)
storage.del_file(apptodev_obj.binary_file+ ".ipa")
apptodev_obj.binary_file=random_file_name
apptodev_obj.release_file=release_obj.release_id
storage.del_file(apptodev_obj.binary_file + ".ipa")
apptodev_obj.binary_file = random_file_name
apptodev_obj.release_file = release_obj.release_id
apptodev_obj.save()
else:
APPToDeveloper.objects.create(developerid=self.developer_obj,app_id=self.app_obj,binary_file=random_file_name,release_file=release_obj.release_id)
APPToDeveloper.objects.create(developerid=self.developer_obj, app_id=self.app_obj,
binary_file=random_file_name, release_file=release_obj.release_id)
@staticmethod
def disable_udid(udid_obj,app_id):
def disable_udid(udid_obj, app_id):
usedeviceobj=APPSuperSignUsedInfo.objects.filter(udid=udid_obj,app_id_id=app_id)
usedeviceobj = APPSuperSignUsedInfo.objects.filter(udid=udid_obj, app_id_id=app_id)
if usedeviceobj:
developer_obj = usedeviceobj.first().developerid
auth = {
@ -234,13 +241,13 @@ class IosUtils(object):
}
# 需要判断该设备在同一个账户下 的多个应用,若存在,则不操作
udid_lists=list(APPSuperSignUsedInfo.objects.values_list("udid__udid").filter(developerid=developer_obj))
udid_lists = list(APPSuperSignUsedInfo.objects.values_list("udid__udid").filter(developerid=developer_obj))
if udid_lists.count((udid_obj.udid,)) == 1:
app_api_obj = AppDeveloperApi(**auth)
app_api_obj.set_device_status("disable",udid_obj.udid)
app_api_obj.set_device_status("disable", udid_obj.udid)
if developer_obj.use_number > 0:
developer_obj.use_number=developer_obj.use_number-1
developer_obj.use_number = developer_obj.use_number - 1
developer_obj.save()
usedeviceobj.delete()
@ -249,15 +256,14 @@ class IosUtils(object):
if APPSuperSignUsedInfo.objects.filter(developerid=developer_obj, app_id_id=app_id).count() == 0:
app_api_obj = AppDeveloperApi(**auth)
app_obj = Apps.objects.filter(pk=app_id).first()
app_api_obj.del_profile(app_obj.bundle_id,app_obj.app_id)
app_api_obj.del_profile(app_obj.bundle_id, app_obj.app_id)
app_api_obj2 = AppDeveloperApi(**auth)
app_api_obj2.del_app(app_obj.bundle_id,app_obj.app_id)
app_api_obj2.del_app(app_obj.bundle_id, app_obj.app_id)
delete_app_to_dev_and_file(developer_obj, app_id)
delete_app_profile_file(developer_obj, app_obj)
@staticmethod
def clean_udid_by_app_obj(app_obj,developer_obj):
def clean_udid_by_app_obj(app_obj, developer_obj):
auth = {
"username": developer_obj.email,
@ -265,7 +271,6 @@ class IosUtils(object):
"certid": developer_obj.certid
}
# 需要判断该设备在同一个账户下 的多个应用,若存在,则不操作
udid_lists = list(APPSuperSignUsedInfo.objects.values_list("udid__udid").filter(developerid=developer_obj))
@ -284,27 +289,26 @@ class IosUtils(object):
SuperSignUsed_obj.delete()
@staticmethod
def clean_app_by_user_obj(app_obj,user_obj):
def clean_app_by_user_obj(app_obj, user_obj):
'''
该APP为超级签删除app的时候需要清理一下开发者账户里面的profile bundleid
:param app_obj:
:param user_obj:
:return:
'''
SuperSign_obj_lists=list(set(APPSuperSignUsedInfo.objects.values_list("developerid").filter(user_id=user_obj, app_id=app_obj)))
SuperSign_obj_lists = list(
set(APPSuperSignUsedInfo.objects.values_list("developerid").filter(user_id=user_obj, app_id=app_obj)))
for SuperSign_obj in SuperSign_obj_lists:
developer_obj=AppIOSDeveloperInfo.objects.filter(pk=SuperSign_obj[0]).first()
developer_obj = AppIOSDeveloperInfo.objects.filter(pk=SuperSign_obj[0]).first()
if developer_obj:
IosUtils.clean_app_by_developer_obj(app_obj,developer_obj)
IosUtils.clean_app_by_developer_obj(app_obj, developer_obj)
delete_app_to_dev_and_file(developer_obj, app_obj.id)
IosUtils.clean_udid_by_app_obj(app_obj,developer_obj)
IosUtils.clean_udid_by_app_obj(app_obj, developer_obj)
delete_app_profile_file(developer_obj, app_obj)
@staticmethod
def clean_app_by_developer_obj(app_obj,developer_obj):
def clean_app_by_developer_obj(app_obj, developer_obj):
auth = {
"username": developer_obj.email,
"password": developer_obj.password,
@ -313,21 +317,21 @@ class IosUtils(object):
app_api_obj = AppDeveloperApi(**auth)
app_api_obj.del_profile(app_obj.bundle_id, app_obj.app_id)
app_api_obj2 = AppDeveloperApi(**auth)
app_api_obj2.del_app(app_obj.bundle_id,app_obj.app_id)
app_api_obj2.del_app(app_obj.bundle_id, app_obj.app_id)
@staticmethod
def clean_developer(developer_obj,user_obj):
def clean_developer(developer_obj, user_obj):
'''
根据消耗记录 删除该苹果账户下所有信息
:param developer_obj:
:return:
'''
for APPToDeveloper_obj in APPToDeveloper.objects.filter(developerid=developer_obj):
app_obj=APPToDeveloper_obj.app_id
app_obj = APPToDeveloper_obj.app_id
IosUtils.clean_app_by_developer_obj(app_obj, developer_obj)
delete_app_to_dev_and_file(developer_obj,app_obj.id)
delete_app_to_dev_and_file(developer_obj, app_obj.id)
IosUtils.clean_udid_by_app_obj(app_obj, developer_obj)
full_path=file_format_path(user_obj, email=developer_obj.email)
full_path = file_format_path(user_obj, email=developer_obj.email)
try:
for root, dirs, files in os.walk(full_path, topdown=False):
for name in files:
@ -338,9 +342,8 @@ class IosUtils(object):
except Exception as e:
print(e)
@staticmethod
def active_developer(developer_obj,user_obj):
def active_developer(developer_obj, user_obj):
'''
激活开发者账户
:param developer_obj:
@ -353,26 +356,23 @@ class IosUtils(object):
"certid": developer_obj.certid
}
app_api_obj = AppDeveloperApi(**auth)
status,result = app_api_obj.active(user_obj)
status, result = app_api_obj.active(user_obj)
if status:
developer_obj.is_actived=True
developer_obj.is_actived = True
developer_obj.save()
if not developer_obj.certid :
IosUtils.create_developer_cert(developer_obj,user_obj)
IosUtils.get_device_from_developer(developer_obj, user_obj)
return status,result
return status, result
@staticmethod
def create_developer_cert(developer_obj,user_obj):
def create_developer_cert(developer_obj, user_obj):
auth = {
"username": developer_obj.email,
"password": developer_obj.password,
"certid": developer_obj.certid
}
app_api_obj = AppDeveloperApi(**auth)
status,result = app_api_obj.create_cert(user_obj)
status, result = app_api_obj.create_cert(user_obj)
if status:
file_format_path_name = file_format_path(user_obj,auth)
file_format_path_name = file_format_path(user_obj, auth)
cert_info = None
try:
with open(file_format_path_name + '.info', "r") as f:
@ -380,26 +380,26 @@ class IosUtils(object):
except Exception as e:
print(e)
cert_id = re.findall(r'.*\n\tid=(.*),.*', cert_info)[0].replace('"', '')
developer_obj=AppIOSDeveloperInfo.objects.filter(user_id=user_obj, email=auth.get("username")).first()
developer_obj.is_actived=True
developer_obj.certid=cert_id
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=user_obj, email=auth.get("username")).first()
developer_obj.is_actived = True
developer_obj.certid = cert_id
developer_obj.save()
return status,result
return status, result
@staticmethod
def get_device_from_developer(developer_obj,user_obj):
def get_device_from_developer(developer_obj, user_obj):
auth = {
"username": developer_obj.email,
"password": developer_obj.password,
"certid": developer_obj.certid
}
app_api_obj = AppDeveloperApi(**auth)
status,result = app_api_obj.get_device(user_obj)
status, result = app_api_obj.get_device(user_obj)
if status:
file_format_path_name = file_format_path(user_obj,auth)
file_format_path_name = file_format_path(user_obj, auth)
devices_info = ""
try:
with open(file_format_path_name+".devices.info", "r") as f:
with open(file_format_path_name + ".devices.info", "r") as f:
devices_info = f.read().replace("\n\t", "").replace("[", "").replace("]", "")
except Exception as e:
print(e)
@ -413,9 +413,9 @@ class IosUtils(object):
"udid": formatdevice[0][2],
"version": formatdevice[0][3],
}
app_udid_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj,udid=device.get("udid"))
app_udid_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj, udid=device.get("udid"))
if app_udid_obj:
pass
else:
UDIDsyncDeveloper.objects.create(developerid=developer_obj,**device)
return status,result
UDIDsyncDeveloper.objects.create(developerid=developer_obj, **device)
return status, result

@ -5,6 +5,7 @@ from api.models import Token, UserInfo
from fir_ser.settings import CACHE_KEY_TEMPLATE
import base64
class ExpiringTokenAuthentication(BaseAuthentication):
def authenticate(self, request):
@ -27,16 +28,16 @@ class ExpiringTokenAuthentication(BaseAuthentication):
cacheuserinfo = cache.get(auth_key)
if not cacheuserinfo:
raise AuthenticationFailed({"code": 1001, "error": "无效的token"})
if user_name != cacheuserinfo.get('username',None):
if user_name != cacheuserinfo.get('username', None):
raise AuthenticationFailed({"code": 1001, "error": "token校验失败"})
user_obj = UserInfo.objects.filter(uid=cacheuserinfo.get('uid',None),username=cacheuserinfo.get("username")).first()
user_obj = UserInfo.objects.filter(uid=cacheuserinfo.get('uid', None),
username=cacheuserinfo.get("username")).first()
# token_obj = Token.objects.filter(access_token=auth_token).first()
if not user_obj:
raise AuthenticationFailed({"code": 1001, "error": "无效的token"})
if user_obj.is_active:
cache.set(auth_key, cacheuserinfo, 3600 * 24 * 7)
return user_obj,auth_token
return user_obj, auth_token
else:
raise AuthenticationFailed({"code": 1001, "error": "用户被禁用"})

@ -1,5 +1,7 @@
from django.conf import settings
from api.utils.geetest import GeeTestLib
def verify(verify_data, uid=None, extend_params=None):
"""第三方滑动验证码校验.
@ -44,4 +46,3 @@ def verify(verify_data, uid=None, extend_params=None):
return True if result else False
else:
return True

@ -11,7 +11,7 @@ from fir_ser.settings import CACHE_KEY_TEMPLATE
def sync_download_times():
down_tem_key = CACHE_KEY_TEMPLATE.get("download_times_key")
key = "_".join([down_tem_key,'*'])
key = "_".join([down_tem_key, '*'])
for app_download in cache.iter_keys(key):
app_id = app_download.split(down_tem_key)[1].strip('_')
Apps.objects.filter(app_id=app_id).update(count_hits=cache.get(app_download))
@ -20,12 +20,9 @@ def sync_download_times():
def sync_download_times_by_app_id(app_ids):
app_id_lists = []
for app_id in app_ids:
down_tem_key = "_".join([CACHE_KEY_TEMPLATE.get("download_times_key"),app_id.get("app_id")])
down_tem_key = "_".join([CACHE_KEY_TEMPLATE.get("download_times_key"), app_id.get("app_id")])
app_id_lists.append(down_tem_key)
down_times_lists = cache.get_many(app_id_lists)
for k, v in down_times_lists.items():
app_id = k.split(CACHE_KEY_TEMPLATE.get("download_times_key"))[1].strip('_')
Apps.objects.filter(app_id=app_id).update(count_hits=v)

@ -1,12 +1,10 @@
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleWare(MiddlewareMixin):
def process_response(self,request,response):
class CorsMiddleWare(MiddlewareMixin):
if request.method=="OPTIONS":
def process_response(self, request, response):
if request.method == "OPTIONS":
response["Access-Control-Allow-Methods"] = "GET,POST,DELETE,PUT"
response["Access-Control-Allow-Headers"] = "Content-Type,AUTHORIZATION"

@ -1,5 +1,3 @@
from django.core.cache import cache
import datetime
@ -10,12 +8,12 @@ from api.models import Token
from rest_framework.permissions import BasePermission
class LoginUserPermission(BaseAuthentication):
def has_permission(self,request,view):
def has_permission(self, request, view):
if request.user:
return True
else:
return False

@ -1,13 +1,9 @@
class BaseResponse(object):
def __init__(self):
self.code=1000
self.msg=""
self.data=None
self.code = 1000
self.msg = ""
self.data = None
@property
def dict(self):
return self.__dict__

@ -4,19 +4,22 @@ from api.utils.app.apputils import bytes2human
from api.utils.TokenManager import DownloadToken
from api.utils.storage.storage import Storage
from api.utils.utils import get_developer_udided
import os,json
import os, json
token_obj = DownloadToken()
class UserInfoSerializer(serializers.ModelSerializer):
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
# fields="__all__"
# exclude = ["password","is_active","user_permissions","role",]
fields=["username","uid","qq","mobile","job","email","domain_name","last_login","first_name",'head_img']
fields = ["username", "uid", "qq", "mobile", "job", "email", "domain_name", "last_login", "first_name",
'head_img']
head_img = serializers.SerializerMethodField()
def get_head_img(self,obj):
def get_head_img(self, obj):
storage = Storage(obj)
return storage.get_download_url(obj.head_img)
@ -32,7 +35,7 @@ class AppsSerializer(serializers.ModelSerializer):
def get_has_combo(self, obj):
if obj.has_combo:
obj.has_combo.has_combo = None
return AppsSerializer(obj.has_combo,context=self.context).data
return AppsSerializer(obj.has_combo, context=self.context).data
master_release = serializers.SerializerMethodField()
@ -44,12 +47,12 @@ class AppsSerializer(serializers.ModelSerializer):
if master_release_obj:
icon_url = ""
key=''
key = ''
if self.context.get("key", None) and self.context.get("key") != "undefined":
key=self.context.get("key", '')
key = self.context.get("key", '')
if self.context.get("storage", None) and self.context.get("storage") != "undefined":
storage = self.context.get("storage", None)
icon_url = storage.get_download_url(os.path.basename(master_release_obj.icon_url),600,key=key)
icon_url = storage.get_download_url(os.path.basename(master_release_obj.icon_url), 600, key=key)
datainfo = {
"app_version": master_release_obj.app_version,
"icon_url": icon_url,
@ -60,16 +63,16 @@ class AppsSerializer(serializers.ModelSerializer):
"binary_size": bytes2human(master_release_obj.binary_size),
"release_id": master_release_obj.release_id,
"changelog": master_release_obj.changelog,
"binary_url":master_release_obj.binary_url,
"binary_url": master_release_obj.binary_url,
}
download_token = token_obj.make_token(master_release_obj.release_id,600,key=key)
download_token = token_obj.make_token(master_release_obj.release_id, 600, key=key)
datainfo["download_token"] = download_token
udid_lists = []
try:
udid_data = eval(master_release_obj.udid)
for udid in udid_data:
udid_lists.append({'udid':udid})
udid_lists.append({'udid': udid})
except Exception as e:
pass
datainfo["udid"] = udid_lists
@ -79,24 +82,25 @@ class AppsSerializer(serializers.ModelSerializer):
return {}
class AppsShortSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
fields = ["app_id","name","short","has_combo","isshow","description","need_password",'master_release','type','issupersign']
fields = ["app_id", "name", "short", "has_combo", "isshow", "description", "need_password", 'master_release',
'type', 'issupersign']
need_password=serializers.SerializerMethodField()
need_password = serializers.SerializerMethodField()
def get_need_password(self,obj):
def get_need_password(self, obj):
if obj.password != '':
return True
return False
has_combo = serializers.SerializerMethodField()
def get_has_combo(self, obj):
if obj.has_combo:
obj.has_combo.has_combo = None
return AppsSerializer(obj.has_combo,context=self.context).data
return AppsSerializer(obj.has_combo, context=self.context).data
master_release = serializers.SerializerMethodField()
@ -108,12 +112,13 @@ class AppsShortSerializer(serializers.ModelSerializer):
if master_release_obj:
icon_url = ""
key=''
key = ''
if self.context.get("key", None) and self.context.get("key") != "undefined":
key=self.context.get("key", '')
key = self.context.get("key", '')
if self.context.get("storage", None) and self.context.get("storage") != "undefined":
storage = self.context.get("storage", None)
icon_url = storage.get_download_url(os.path.basename(master_release_obj.icon_url),600,key=key,force_new=True)
icon_url = storage.get_download_url(os.path.basename(master_release_obj.icon_url), 600, key=key,
force_new=True)
datainfo = {
"app_version": master_release_obj.app_version,
"icon_url": icon_url,
@ -123,10 +128,10 @@ class AppsShortSerializer(serializers.ModelSerializer):
"binary_size": bytes2human(master_release_obj.binary_size),
"release_id": master_release_obj.release_id,
"changelog": master_release_obj.changelog,
"binary_url":master_release_obj.binary_url,
"binary_url": master_release_obj.binary_url,
}
download_token = token_obj.make_token(master_release_obj.release_id,600,key=key,force_new=True)
download_token = token_obj.make_token(master_release_obj.release_id, 600, key=key, force_new=True)
datainfo["download_token"] = download_token
return datainfo
else:
@ -139,17 +144,18 @@ class AppReleaseSerializer(serializers.ModelSerializer):
fields = ["app_version", "icon_url", "build_version",
"release_type", "minimum_os_version",
"created_time", "binary_size", "release_id", "size", "type", "editing", "master_color", "changelog",
"is_master",'download_token','binary_url','udid']
"is_master", 'download_token', 'binary_url', 'udid']
download_token = serializers.SerializerMethodField()
size = serializers.SerializerMethodField()
type = serializers.SerializerMethodField()
editing = serializers.SerializerMethodField()
master_color = serializers.SerializerMethodField()
icon_url = serializers.SerializerMethodField()
binary_size= serializers.SerializerMethodField()
udid= serializers.SerializerMethodField()
binary_size = serializers.SerializerMethodField()
udid = serializers.SerializerMethodField()
def get_udid(self,obj):
def get_udid(self, obj):
udid_lists = []
try:
udid_data = eval(obj.udid)
@ -159,23 +165,23 @@ class AppReleaseSerializer(serializers.ModelSerializer):
pass
return udid_lists
def get_binary_size(self,obj):
def get_binary_size(self, obj):
return bytes2human(obj.binary_size)
def get_download_token(self,obj):
def get_download_token(self, obj):
download_token = token_obj.make_token(obj.release_id, 300)
return download_token
def get_icon_url(self, obj):
icon_url=""
icon_url = ""
key = ''
if self.context.get("key", None) and self.context.get("key") != "undefined":
key = self.context.get("key", '')
if self.context.get("storage", None) and self.context.get("storage") != "undefined":
storage = self.context.get("storage", None)
icon_url = storage.get_download_url(os.path.basename(obj.icon_url),600,key=key)
icon_url = storage.get_download_url(os.path.basename(obj.icon_url), 600, key=key)
return icon_url
@ -190,7 +196,7 @@ class AppReleaseSerializer(serializers.ModelSerializer):
return "primary"
def get_editing(self, obj):
return {"changelog":False,"binary_url":False}
return {"changelog": False, "binary_url": False}
class StorageSerializer(serializers.ModelSerializer):
@ -198,12 +204,12 @@ class StorageSerializer(serializers.ModelSerializer):
model = models.AppStorage
exclude = ["user_id"]
storage_type_display= serializers.CharField(source="get_storage_type_display",read_only=True)
additionalparameters=serializers.CharField(write_only=True)
additionalparameter=serializers.SerializerMethodField(read_only=True)
storage_type_display = serializers.CharField(source="get_storage_type_display", read_only=True)
additionalparameters = serializers.CharField(write_only=True)
additionalparameter = serializers.SerializerMethodField(read_only=True)
def get_additionalparameter(self,obj):
infos={}
def get_additionalparameter(self, obj):
infos = {}
try:
infos = json.loads(obj.additionalparameters)
except Exception as e:
@ -213,29 +219,32 @@ class StorageSerializer(serializers.ModelSerializer):
def create(self, validated_data):
if self.context.get("user_obj", None) and self.context.get("user_obj") != "undefined":
user_obj = self.context.get("user_obj", None)
storage_obj = models.AppStorage.objects.create(**validated_data,user_id=user_obj,)
storage_obj = models.AppStorage.objects.create(**validated_data, user_id=user_obj, )
return storage_obj
return None
class DeveloperSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppIOSDeveloperInfo
# depth = 1
exclude = ["password", "id","user_id"]
exclude = ["password", "id", "user_id"]
developer_used_number = serializers.SerializerMethodField()
developer_used_other_number = serializers.SerializerMethodField()
def get_developer_used_number(self,obj):
def get_developer_used_number(self, obj):
return models.UDIDsyncDeveloper.objects.filter(developerid=obj).count()
def get_developer_used_other_number(self,obj):
def get_developer_used_other_number(self, obj):
return get_developer_udided(obj)[0]
class SuperSignUsedSerializer(serializers.ModelSerializer):
class Meta:
model = models.APPSuperSignUsedInfo
# depth = 1
fields = ["created_time","device_udid","device_name","developer_id","bundle_id","bundle_name"]
fields = ["created_time", "device_udid", "device_name", "developer_id", "bundle_id", "bundle_name"]
device_udid = serializers.SerializerMethodField()
device_name = serializers.SerializerMethodField()
@ -243,13 +252,13 @@ class SuperSignUsedSerializer(serializers.ModelSerializer):
bundle_id = serializers.SerializerMethodField()
bundle_name = serializers.SerializerMethodField()
def get_device_udid(self,obj):
def get_device_udid(self, obj):
return obj.udid.udid
def get_device_name(self,obj):
def get_device_name(self, obj):
return obj.udid.product
def get_developer_id(self,obj):
def get_developer_id(self, obj):
return obj.developerid.email
def get_bundle_id(self, obj):
@ -263,7 +272,7 @@ class DeviceUDIDSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppUDID
# depth = 1
exclude = ["binary_file","updated_time","is_signed"]
exclude = ["binary_file", "updated_time", "is_signed"]
bundle_id = serializers.SerializerMethodField()
bundle_name = serializers.SerializerMethodField()

@ -4,7 +4,7 @@
# author: liuyu
# date: 2020/3/22
#pip install aliyun-python-sdk-sts oss2
# pip install aliyun-python-sdk-sts oss2
import json
import os
@ -40,31 +40,32 @@ class StsToken(object):
:param str security_token: 临时用户Token
:param str request_id: 请求ID
"""
def __init__(self):
self.access_key_id = ''
self.access_key_secret = ''
self.expiration = 3600
self.security_token = ''
self.request_id = ''
self.bucket=''
self.endpoint=''
self.bucket = ''
self.endpoint = ''
class AliYunOss(object):
def __init__(self,access_key,secret_key,bucket_name,endpoint,sts_role_arn,is_https,domain_name=None):
def __init__(self, access_key, secret_key, bucket_name, endpoint, sts_role_arn, is_https, domain_name=None):
self.access_key_id = access_key or os.getenv('OSS_TEST_STS_ID', 'LTAI4FeTyvz74CHKCbYuTDGc')
self.access_key_secret = secret_key or os.getenv('OSS_TEST_STS_KEY', 'uineAEE7tp1d1w4Mv0yLqxvV0IVNTy')
self.bucket_name = bucket_name or os.getenv('OSS_TEST_BUCKET', 'fir-storage')
self.endpoint = endpoint or os.getenv('OSS_TEST_ENDPOINT', 'oss-cn-beijing.aliyuncs.com')
self.sts_role_arn = sts_role_arn or os.getenv('OSS_TEST_STS_ARN', 'acs:ram::1622120977387033:role/appstorage')
self.region_id = '-'.join( self.endpoint.split('.')[0].split("-")[1:3])
self.region_id = '-'.join(self.endpoint.split('.')[0].split("-")[1:3])
self.token = StsToken()
self.is_https = is_https
self.domain_name = domain_name
self.get_auth_bucket('init', 900)
def fetch_sts_token(self,name,expires):
def fetch_sts_token(self, name, expires):
"""子用户角色扮演获取临时用户的密钥
:param access_key_id: 子用户的 access key id
:param access_key_secret: 子用户的 access key secret
@ -88,33 +89,33 @@ class AliYunOss(object):
self.token.security_token = j['Credentials']['SecurityToken']
self.token.request_id = j['RequestId']
self.token.expiration = oss2.utils.to_unixtime(j['Credentials']['Expiration'], '%Y-%m-%dT%H:%M:%SZ')
self.token.bucket=self.bucket_name
self.token.endpoint=self.endpoint
self.token.bucket = self.bucket_name
self.token.endpoint = self.endpoint
return self.token.__dict__
def get_upload_token(self,name,expires=1800):
return self.fetch_sts_token(name,expires)
def get_upload_token(self, name, expires=1800):
return self.fetch_sts_token(name, expires)
def get_auth_bucket(self,name,expires):
self.fetch_sts_token(name,expires)
def get_auth_bucket(self, name, expires):
self.fetch_sts_token(name, expires)
uri = 'http://'
if self.is_https:
uri = 'https://'
url = self.endpoint
is_cname=False
is_cname = False
if self.domain_name:
url = self.domain_name
is_cname=True
is_cname = True
auth = oss2.StsAuth(self.token.access_key_id, self.token.access_key_secret, self.token.security_token)
self.bucket = oss2.Bucket(auth,uri+url,self.bucket_name,is_cname=is_cname)
self.bucket = oss2.Bucket(auth, uri + url, self.bucket_name, is_cname=is_cname)
def get_download_url(self,name,expires=1800,ftype=None,force_new=False):
def get_download_url(self, name, expires=1800, ftype=None, force_new=False):
# self.get_auth_bucket(name,expires)
private_url=self.bucket.sign_url('GET',name,expires)
private_url = self.bucket.sign_url('GET', name, expires)
return private_url
def del_file(self,name):
def del_file(self, name):
# self.fetch_sts_token(name,expires=300)
# auth = oss2.StsAuth(self.token.access_key_id, self.token.access_key_secret, self.token.security_token)
# bucket = oss2.Bucket(auth, self.endpoint,self.bucket_name)

@ -5,33 +5,39 @@
# date: 2020/4/7
from django.core.cache import cache
from api.models import Apps,UserInfo,AppReleaseInfo,AppUDID,APPToDeveloper,APPSuperSignUsedInfo
import time,os
from api.models import Apps, UserInfo, AppReleaseInfo, AppUDID, APPToDeveloper, APPSuperSignUsedInfo
import time, os
from django.utils import timezone
from fir_ser.settings import CACHE_KEY_TEMPLATE,SERVER_DOMAIN
from api.utils.storage.storage import Storage,LocalStorage
from fir_ser.settings import CACHE_KEY_TEMPLATE, SERVER_DOMAIN
from api.utils.storage.storage import Storage, LocalStorage
from api.utils.crontab.sync_cache import sync_download_times_by_app_id
from api.utils.utils import file_format_path
try:
from api.utils.crontab import run
except Exception as e:
print(e)
def get_download_url_by_cache(app_obj, filename, limit, isdownload=True,key='',udid=None):
def get_download_url_by_cache(app_obj, filename, limit, isdownload=True, key='', udid=None):
now = time.time()
if isdownload is None:
local_storage = LocalStorage(**SERVER_DOMAIN.get("IOS_PMFILE_DOWNLOAD_DOMAIN"))
download_url_type = 'plist'
if not udid:
if app_obj.get('issupersign',None):
if app_obj.get('issupersign', None):
download_url_type = 'mobileconifg'
else:
appudid_obj = AppUDID.objects.filter(app_id_id=app_obj.get("pk"),udid=udid,is_signed=True).first()
appudid_obj = AppUDID.objects.filter(app_id_id=app_obj.get("pk"), udid=udid, is_signed=True).first()
if appudid_obj:
SuperSign_obj = APPSuperSignUsedInfo.objects.filter(udid__udid=udid,app_id_id=app_obj.get("pk")).first()
SuperSign_obj = APPSuperSignUsedInfo.objects.filter(udid__udid=udid,
app_id_id=app_obj.get("pk")).first()
if SuperSign_obj:
APPToDeveloper_obj = APPToDeveloper.objects.filter(app_id_id=app_obj.get("pk"),developerid=SuperSign_obj.developerid).first()
APPToDeveloper_obj = APPToDeveloper.objects.filter(app_id_id=app_obj.get("pk"),
developerid=SuperSign_obj.developerid).first()
if APPToDeveloper_obj:
return local_storage.get_download_url(APPToDeveloper_obj.binary_file+"."+filename.split(".")[-1], limit, download_url_type)
return local_storage.get_download_url(
APPToDeveloper_obj.binary_file + "." + filename.split(".")[-1], limit, download_url_type)
else:
return ""
else:
@ -51,16 +57,17 @@ def get_download_url_by_cache(app_obj, filename, limit, isdownload=True,key='',u
return storage.get_download_url(filename, limit)
def get_app_instance_by_cache(app_id,password, limit,udid):
def get_app_instance_by_cache(app_id, password, limit, udid):
if udid:
return Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type','password','issupersign').first()
app_key="_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"),app_id])
return Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type', 'password', 'issupersign').first()
app_key = "_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"), app_id])
app_obj_cache = cache.get(app_key)
if not app_obj_cache:
app_obj_cache = Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type','password','issupersign').first()
app_obj_cache = Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type', 'password',
'issupersign').first()
cache.set(app_key, app_obj_cache, limit)
app_password=app_obj_cache.get("password")
app_password = app_obj_cache.get("password")
if app_password != '':
if password is None:
@ -73,10 +80,10 @@ def get_app_instance_by_cache(app_id,password, limit,udid):
def set_app_download_by_cache(app_id, limit=900):
down_tem_key = "_".join([CACHE_KEY_TEMPLATE.get("download_times_key"),app_id])
down_tem_key = "_".join([CACHE_KEY_TEMPLATE.get("download_times_key"), app_id])
download_times = cache.get(down_tem_key)
if not download_times:
download_times=Apps.objects.filter(app_id=app_id).values("count_hits").first().get('count_hits')
download_times = Apps.objects.filter(app_id=app_id).values("count_hits").first().get('count_hits')
cache.set(down_tem_key, download_times + 1, limit)
else:
cache.incr(down_tem_key)
@ -84,73 +91,80 @@ def set_app_download_by_cache(app_id, limit=900):
set_app_today_download_times(app_id)
return download_times + 1
def del_cache_response_by_short(short,app_id,udid=''):
cache.delete("_".join([CACHE_KEY_TEMPLATE.get("download_short_key"),short]))
key = "_".join([CACHE_KEY_TEMPLATE.get("download_short_key"),short,'*'])
def del_cache_response_by_short(short, app_id, udid=''):
cache.delete("_".join([CACHE_KEY_TEMPLATE.get("download_short_key"), short]))
key = "_".join([CACHE_KEY_TEMPLATE.get("download_short_key"), short, '*'])
for app_download_key in cache.iter_keys(key):
cache.delete(app_download_key)
cache.delete("_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"),app_id]))
cache.delete("_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"), app_id]))
key='ShortDownloadView'.lower()
master_release_dict = AppReleaseInfo.objects.filter(app_id__app_id=app_id,is_master=True).values('icon_url','release_id').first()
key = 'ShortDownloadView'.lower()
master_release_dict = AppReleaseInfo.objects.filter(app_id__app_id=app_id, is_master=True).values('icon_url',
'release_id').first()
if master_release_dict:
download_val = CACHE_KEY_TEMPLATE.get('download_url_key')
cache.delete("_".join([key, download_val, os.path.basename(master_release_dict.get("icon_url")),udid]))
cache.delete("_".join([key,download_val, master_release_dict.get('release_id'),udid]))
cache.delete("_".join([key, CACHE_KEY_TEMPLATE.get("make_token_key"), master_release_dict.get('release_id'),udid]))
cache.delete("_".join([key, download_val, os.path.basename(master_release_dict.get("icon_url")), udid]))
cache.delete("_".join([key, download_val, master_release_dict.get('release_id'), udid]))
cache.delete(
"_".join([key, CACHE_KEY_TEMPLATE.get("make_token_key"), master_release_dict.get('release_id'), udid]))
def del_cache_by_app_id(app_id,user_obj):
key=''
master_release_dict = AppReleaseInfo.objects.filter(app_id__app_id=app_id,is_master=True).values('icon_url','release_id').first()
def del_cache_by_app_id(app_id, user_obj):
key = ''
master_release_dict = AppReleaseInfo.objects.filter(app_id__app_id=app_id, is_master=True).values('icon_url',
'release_id').first()
download_val = CACHE_KEY_TEMPLATE.get('download_url_key')
cache.delete("_".join([key, download_val, os.path.basename(master_release_dict.get("icon_url"))]))
cache.delete("_".join([key,download_val, master_release_dict.get('release_id')]))
cache.delete("_".join([key.lower(), CACHE_KEY_TEMPLATE.get("make_token_key"), master_release_dict.get('release_id')]))
cache.delete("_".join([key,download_val,user_obj.head_img]))
cache.delete("_".join([key, download_val, master_release_dict.get('release_id')]))
cache.delete(
"_".join([key.lower(), CACHE_KEY_TEMPLATE.get("make_token_key"), master_release_dict.get('release_id')]))
cache.delete("_".join([key, download_val, user_obj.head_img]))
def del_cache_storage(user_obj):
for app_obj in Apps.objects.filter(user_id=user_obj):
del_cache_response_by_short(app_obj.short, app_obj.app_id)
del_cache_by_app_id(app_obj.app_id,user_obj)
del_cache_by_app_id(app_obj.app_id, user_obj)
storage_keys = "_".join([CACHE_KEY_TEMPLATE.get('user_storage_key'),user_obj.uid,'*'])
storage_keys = "_".join([CACHE_KEY_TEMPLATE.get('user_storage_key'), user_obj.uid, '*'])
for storage_key in cache.iter_keys(storage_keys):
cache.delete(storage_key)
def set_app_today_download_times(app_id):
now = timezone.now()
down_tem_key = "_".join([CACHE_KEY_TEMPLATE.get("download_today_times_key"),
str(now.year),str(now.month),str(now.day),app_id])
str(now.year), str(now.month), str(now.day), app_id])
if cache.get(down_tem_key):
cache.incr(down_tem_key)
else:
cache.set(down_tem_key,1,3600*24)
cache.set(down_tem_key, 1, 3600 * 24)
def get_app_today_download_times(app_ids):
sync_download_times_by_app_id(app_ids)
now = timezone.now()
app_id_lists=[]
download_times_count=0
app_id_lists = []
download_times_count = 0
for app_id in app_ids:
down_tem_key = "_".join([CACHE_KEY_TEMPLATE.get("download_today_times_key"),
str(now.year),str(now.month),str(now.day),app_id.get("app_id")])
str(now.year), str(now.month), str(now.day), app_id.get("app_id")])
app_id_lists.append(down_tem_key)
down_times_lists = cache.get_many(app_id_lists)
for k, v in down_times_lists.items():
download_times_count+=v
download_times_count += v
return download_times_count
def developer_auth_code(act,user_obj,developer_email,code=None):
auth_key=file_format_path(user_obj,email=developer_email)
key = "_".join([CACHE_KEY_TEMPLATE.get("developer_auth_code_key"),auth_key])
def developer_auth_code(act, user_obj, developer_email, code=None):
auth_key = file_format_path(user_obj, email=developer_email)
key = "_".join([CACHE_KEY_TEMPLATE.get("developer_auth_code_key"), auth_key])
if act == "set":
cache.delete(key)
cache.set(key,code,60*10)
cache.set(key, code, 60 * 10)
elif act == "get":
return cache.get(key)
elif act == "del":

@ -10,31 +10,31 @@ from api.utils.TokenManager import DownloadToken
from fir_ser import settings
import os
class LocalStorage(object):
def __init__(self,domain_name,is_https):
def __init__(self, domain_name, is_https):
self.domain_name = domain_name
self.is_https = is_https
def get_upload_token(self,name,expires):
def get_upload_token(self, name, expires):
dtoken = DownloadToken()
return dtoken.make_token(name,expires)
return dtoken.make_token(name, expires)
def get_download_url(self,name,expires=1800,ftype=None,force_new=False):
def get_download_url(self, name, expires=1800, ftype=None, force_new=False):
dtoken = DownloadToken()
base_url = '/'.join([self.domain_name,'download', name])
uri='http://'
base_url = '/'.join([self.domain_name, 'download', name])
uri = 'http://'
if self.is_https:
uri='https://'
download_url = uri+ base_url + "?token=" + dtoken.make_token(name,expires,force_new=force_new)
uri = 'https://'
download_url = uri + base_url + "?token=" + dtoken.make_token(name, expires, force_new=force_new)
if ftype:
download_url = download_url + '&ftype=' + ftype
return download_url
def del_file(self,name):
def del_file(self, name):
try:
os.remove(os.path.join(settings.MEDIA_ROOT, name))
return True
except Exception as e:
print(e)
return False

@ -9,16 +9,17 @@
from qiniu import Auth
from qiniu import BucketManager
class QiNiuOss(object):
def __init__(self,access_key,secret_key,bucket_name,domain_name,is_https):
access_key=access_key
secret_key=secret_key
def __init__(self, access_key, secret_key, bucket_name, domain_name, is_https):
access_key = access_key
secret_key = secret_key
self.bucket_name = bucket_name
self.domain_name = domain_name
self.is_https = is_https
self.qiniu_obj = Auth(access_key,secret_key)
self.qiniu_obj = Auth(access_key, secret_key)
def get_upload_token(self,name,expires=1800):
def get_upload_token(self, name, expires=1800):
# 生成上传 Token,可以指定过期时间等
# 上传策略示例
# https://developer.qiniu.com/kodo/manual/1206/put-policy
@ -32,21 +33,20 @@ class QiNiuOss(object):
# print(token)
return token
def get_download_url(self,name,expires=1800,ftype=None,force_new=False):
#有两种方式构造base_url的形式
def get_download_url(self, name, expires=1800, ftype=None, force_new=False):
# 有两种方式构造base_url的形式
uri = 'http://'
if self.is_https:
uri = 'https://'
base_url = '%s%s/%s' % (uri,self.domain_name, name)
#或者直接输入url的方式下载
#可以设置token过期时间
base_url = '%s%s/%s' % (uri, self.domain_name, name)
# 或者直接输入url的方式下载
# 可以设置token过期时间
private_url = self.qiniu_obj.private_download_url(base_url, expires=expires)
return private_url
def del_file(self,name):
def del_file(self, name):
# 初始化BucketManager
bucket = BucketManager(self.qiniu_obj)
# 删除bucket_name 中的文件 key
ret, info = bucket.delete(self.bucket_name, name)
return ret

@ -18,13 +18,13 @@ class Storage(object):
try:
self.storage = self.get_storage(user)
except Exception as e:
self.storage=None
self.storage = None
def get_upload_token(self, filename, expires=900):
if self.storage:
return self.storage.get_upload_token(filename, expires)
def get_download_url(self, filename, expires=900, ftype=None, key='',force_new=False):
def get_download_url(self, filename, expires=900, ftype=None, key='', force_new=False):
if self.storage:
now = time.time()
down_key = "_".join([key.lower(), CACHE_KEY_TEMPLATE.get('download_url_key'), filename])
@ -33,7 +33,7 @@ class Storage(object):
if download_val.get("time") > now - 60:
return download_val.get("download_url")
download_url = self.storage.get_download_url(filename, expires, ftype,force_new=False)
download_url = self.storage.get_download_url(filename, expires, ftype, force_new=False)
cache.set(down_key, {"download_url": download_url, "time": now + expires}, expires)
return download_url
@ -50,7 +50,7 @@ class Storage(object):
self.storage_obj = user.storage
if self.storage_obj:
auth = self.get_storage_auth(self.storage_obj)
storage_key = "_".join([CACHE_KEY_TEMPLATE.get('user_storage_key'),user.uid,
storage_key = "_".join([CACHE_KEY_TEMPLATE.get('user_storage_key'), user.uid,
base64.b64encode(json.dumps(auth).encode("utf-8")).decode("utf-8")[0:64]])
storage_type = self.storage_obj.storage_type
new_storage_obj = cache.get(storage_key)
@ -70,7 +70,7 @@ class Storage(object):
else:
return self.get_default_storage(user)
def get_default_storage(self,user):
def get_default_storage(self, user):
admin_obj = UserInfo.objects.filter(is_superuser=True).order_by('pk').first()
if admin_obj and admin_obj.storage:
return self.get_storage(admin_obj)
@ -80,7 +80,7 @@ class Storage(object):
if storage.get("active", None):
storage_type = storage.get('type', None)
auth = storage.get('auth', {})
storage_key = "_".join([CACHE_KEY_TEMPLATE.get('user_storage_key'),user.uid,
storage_key = "_".join([CACHE_KEY_TEMPLATE.get('user_storage_key'), user.uid,
base64.b64encode(json.dumps(auth).encode("utf-8")).decode("utf-8")[0:64]])
new_storage_obj = cache.get(storage_key)
if new_storage_obj:

@ -5,13 +5,13 @@
# date: 2020/5/7
import os
from fir_ser.settings import SUPER_SIGN_ROOT
from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo,Apps,APPToDeveloper,UDIDsyncDeveloper
from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \
UDIDsyncDeveloper
from api.utils.app.randomstrings import make_app_uuid
from api.utils.storage.localApi import LocalStorage
from django.db.models import Sum
def file_format_path(user_obj, auth=None, email=None):
if email:
cert_dir_name = make_app_uuid(user_obj, email)
@ -48,16 +48,15 @@ def delete_app_profile_file(developer_obj, app_obj):
def get_developer_udided(developer_obj):
SuperSignUsed_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj )
SuperSignUsed_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj)
UDIDsyncDeveloper_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj)
develoer_udid_lists=[]
supersign_udid_lists=[]
develoer_udid_lists = []
supersign_udid_lists = []
if UDIDsyncDeveloper_obj:
develoer_udid_lists=list(UDIDsyncDeveloper_obj.values_list("udid"))
develoer_udid_lists = list(UDIDsyncDeveloper_obj.values_list("udid"))
if SuperSignUsed_obj:
supersign_udid_lists=list(SuperSignUsed_obj.values_list("udid__udid"))
return len(set(develoer_udid_lists)-set(supersign_udid_lists)),len(develoer_udid_lists)
supersign_udid_lists = list(SuperSignUsed_obj.values_list("udid__udid"))
return len(set(develoer_udid_lists) - set(supersign_udid_lists)), len(develoer_udid_lists)
def get_developer_devices(developer_obj_lists):

@ -8,17 +8,18 @@ from rest_framework.views import APIView
from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication
from rest_framework.response import Response
from django.db.models import Sum,F
from django.db.models import Sum, F
import os
from fir_ser import settings
from api.utils.app.supersignutils import IosUtils
from api.utils.app.randomstrings import make_from_user_uuid
from api.utils.storage.storage import Storage
from api.utils.storage.caches import del_cache_response_by_short,get_app_today_download_times
from api.models import Apps, AppReleaseInfo,APPToDeveloper
from api.utils.storage.caches import del_cache_response_by_short, get_app_today_download_times
from api.models import Apps, AppReleaseInfo, APPToDeveloper
from api.utils.serializer import AppsSerializer, AppReleaseSerializer, UserInfoSerializer
from rest_framework.pagination import PageNumberPagination
class AppsPageNumber(PageNumberPagination):
page_size = 20 # 每页显示多少条
page_size_query_param = 'size' # URL中每页显示条数的参数
@ -59,7 +60,6 @@ class AppsView(APIView):
else:
res.hdata["all_hits_count"] = 0
if app_type == "android":
filter_data = {"user_id": request.user, "type": 0}
@ -104,7 +104,7 @@ class AppInfoView(APIView):
userserializer = UserInfoSerializer(request.user)
res.userinfo = {}
res.userinfo = userserializer.data
count=APPToDeveloper.objects.filter(app_id=apps_obj).count()
count = APPToDeveloper.objects.filter(app_id=apps_obj).count()
res.data["count"] = count
return Response(res.dict)
@ -120,9 +120,9 @@ class AppInfoView(APIView):
storage = Storage(request.user)
has_combo = apps_obj.has_combo
if has_combo:
del_cache_response_by_short(apps_obj.has_combo.short,apps_obj.has_combo.app_id)
del_cache_response_by_short(apps_obj.has_combo.short, apps_obj.has_combo.app_id)
apps_obj.has_combo.has_combo = None
del_cache_response_by_short(apps_obj.short,apps_obj.app_id)
del_cache_response_by_short(apps_obj.short, apps_obj.app_id)
for appreleaseobj in AppReleaseInfo.objects.filter(app_id=apps_obj).all():
storage.delete_file(appreleaseobj.release_id, appreleaseobj.release_type)
storage.delete_file(appreleaseobj.icon_url)
@ -136,10 +136,10 @@ class AppInfoView(APIView):
if app_id:
data = request.data
clean = data.get("clean",None)
if clean :
clean = data.get("clean", None)
if clean:
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
IosUtils.clean_app_by_user_obj(apps_obj,request.user)
IosUtils.clean_app_by_user_obj(apps_obj, request.user)
return Response(res.dict)
has_combo = data.get("has_combo", None)
@ -161,8 +161,8 @@ class AppInfoView(APIView):
has_combo.update(**{"has_combo": apps_obj.first()})
else:
pass
del_cache_response_by_short(apps_obj.first().short,apps_obj.first().app_id)
del_cache_response_by_short(has_combo.first().short,has_combo.first().app_id)
del_cache_response_by_short(apps_obj.first().short, apps_obj.first().app_id)
del_cache_response_by_short(has_combo.first().short, has_combo.first().app_id)
except Exception as e:
res.code = 1004
@ -171,7 +171,7 @@ class AppInfoView(APIView):
try:
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
apps_obj.description = data.get("description", apps_obj.description)
del_cache_response_by_short(apps_obj.short,apps_obj.app_id)
del_cache_response_by_short(apps_obj.short, apps_obj.app_id)
apps_obj.short = data.get("short", apps_obj.short)
apps_obj.name = data.get("name", apps_obj.name)
apps_obj.password = data.get("password", apps_obj.password)
@ -236,7 +236,7 @@ class AppInfoView(APIView):
release_obj.icon_url = release_obj.icon_url.replace(old_file_name.split(".")[0],
random_file_name)
release_obj.save()
del_cache_response_by_short(apps_obj.short,apps_obj.app_id)
del_cache_response_by_short(apps_obj.short, apps_obj.app_id)
storage = Storage(request.user)
storage.delete_file(old_file_name)
@ -295,7 +295,7 @@ class AppReleaseinfoView(APIView):
apps_obj.delete()
else:
pass
del_cache_response_by_short(apps_obj.short,apps_obj.app_id)
del_cache_response_by_short(apps_obj.short, apps_obj.app_id)
return Response(res.dict)
@ -330,7 +330,7 @@ class AppReleaseinfoView(APIView):
res.msg = "更新失败"
return Response(res.dict)
del_cache_response_by_short(apps_obj.short,apps_obj.app_id)
del_cache_response_by_short(apps_obj.short, apps_obj.app_id)
app_serializer = AppsSerializer(apps_obj)
res.data["currentapp"] = app_serializer.data

@ -10,32 +10,36 @@ from fir_ser import settings
from api.utils.TokenManager import DownloadToken
from api.utils.app.randomstrings import make_random_uuid
from api.utils.app.apputils import make_resigned
from api.utils.app.supersignutils import make_udid_mobileconfig,get_post_udid_url
from api.utils.app.supersignutils import make_udid_mobileconfig, get_post_udid_url
from api.utils.storage.storage import Storage
from api.utils.storage.caches import get_app_instance_by_cache,get_download_url_by_cache,set_app_download_by_cache,del_cache_response_by_short
from api.utils.storage.caches import get_app_instance_by_cache, get_download_url_by_cache, set_app_download_by_cache, \
del_cache_response_by_short
import os
from rest_framework_extensions.cache.decorators import cache_response
from api.utils.serializer import AppsShortSerializer
from api.models import Apps,AppReleaseInfo,APPToDeveloper,APPSuperSignUsedInfo
from api.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInfo
from django.http import FileResponse
class DownloadView(APIView):
'''
文件下载接口,适用于本地存储和所有plist文件下载
'''
# authentication_classes = [ExpiringTokenAuthentication, ]
# parser_classes = (MultiPartParser,)
def get(self,request,filename):
def get(self, request, filename):
res = BaseResponse()
downtoken = request.query_params.get("token", None)
ftype = request.query_params.get("ftype",None)
ftype = request.query_params.get("ftype", None)
if not downtoken:
res.code=1004
res.msg="缺失token"
res.code = 1004
res.msg = "缺失token"
return Response(res.dict)
dtoken = DownloadToken()
if dtoken.verify_token(downtoken,filename):
if dtoken.verify_token(downtoken, filename):
if not ftype:
file_path = os.path.join(settings.MEDIA_ROOT, filename)
try:
@ -51,7 +55,7 @@ class DownloadView(APIView):
release_id = filename.split('.')[0]
apptodev_obj = APPToDeveloper.objects.filter(binary_file=release_id).first()
if apptodev_obj:
release_obj = AppReleaseInfo.objects.filter(is_master=True,app_id=apptodev_obj.app_id).first()
release_obj = AppReleaseInfo.objects.filter(is_master=True, app_id=apptodev_obj.app_id).first()
else:
release_obj = AppReleaseInfo.objects.filter(release_id=release_id).first()
if release_obj:
@ -59,7 +63,9 @@ class DownloadView(APIView):
bundle_id = release_obj.app_id.bundle_id
app_version = release_obj.app_version
name = release_obj.app_id.name
ios_plist_bytes = make_resigned(storage.get_download_url(filename),storage.get_download_url(release_obj.icon_url),bundle_id,app_version,name)
ios_plist_bytes = make_resigned(storage.get_download_url(filename),
storage.get_download_url(release_obj.icon_url), bundle_id,
app_version, name)
response = FileResponse(ios_plist_bytes)
response['Content-Type'] = "application/x-plist"
response['Content-Disposition'] = 'attachment; filename=' + make_random_uuid()
@ -69,92 +75,97 @@ class DownloadView(APIView):
release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first()
if release_obj:
bundle_id = release_obj.app_id.bundle_id
udid_url = get_post_udid_url(request,release_obj.app_id.short)
udid_url = get_post_udid_url(request, release_obj.app_id.short)
ios_udid_mobileconfig = make_udid_mobileconfig(udid_url, bundle_id)
response = FileResponse(ios_udid_mobileconfig)
response['Content-Type'] = "application/x-apple-aspen-config"
response['Content-Disposition'] = 'attachment; filename=' + make_random_uuid() +'.mobileconfig'
response['Content-Disposition'] = 'attachment; filename=' + make_random_uuid() + '.mobileconfig'
return response
res.msg = "mobileconifg release_id error"
res.code=1004
res.msg="token校验失败"
res.code = 1004
res.msg = "token校验失败"
return Response(res.dict)
class ShortDownloadView(APIView):
'''
根据下载短链接获取应用信息
'''
@cache_response(timeout=600-60, cache="default",key_func='calculate_cache_key',cache_errors=False)
def get(self,request,short):
@cache_response(timeout=600 - 60, cache="default", key_func='calculate_cache_key', cache_errors=False)
def get(self, request, short):
res = BaseResponse()
release_id = request.query_params.get("release_id", None)
udid = request.query_params.get("udid", None)
app_obj = Apps.objects.filter(short=short).first()
if not app_obj:
res.code=1003
res.msg="该应用不存在"
res.code = 1003
res.msg = "该应用不存在"
return Response(res.dict)
if udid:
del_cache_response_by_short(short,app_obj.app_id,udid=udid)
del_cache_response_by_short(short, app_obj.app_id, udid=udid)
if not app_obj.isshow:
res.code=1004
res.msg="您没有权限访问该应用"
res.code = 1004
res.msg = "您没有权限访问该应用"
return Response(res.dict)
app_serializer = AppsShortSerializer(app_obj,context={"key":"ShortDownloadView","release_id":release_id,"storage":Storage(app_obj.user_id)})
app_serializer = AppsShortSerializer(app_obj, context={"key": "ShortDownloadView", "release_id": release_id,
"storage": Storage(app_obj.user_id)})
res.data = app_serializer.data
res.udid = udid
return Response(res.dict)
#key的设置
# key的设置
def calculate_cache_key(self, view_instance, view_method,
request, args, kwargs):
release_id = request.query_params.get("release_id", '')
udid = request.query_params.get("udid", None)
time = request.query_params.get("time", None)
if udid and time:
udid=time
udid = time
if not udid:
udid=""
udid = ""
return "_".join(
[settings.CACHE_KEY_TEMPLATE.get("download_short_key"), kwargs.get("short", ''), release_id, udid])
return "_".join([settings.CACHE_KEY_TEMPLATE.get("download_short_key"), kwargs.get("short", ''),release_id,udid])
class InstallView(APIView):
'''
安装操作前端通过token 认证认证成功之后返回 下载连接并且 让下载次数加一
'''
def get(self,request,app_id):
def get(self, request, app_id):
res = BaseResponse()
downtoken = request.query_params.get("token", None)
short = request.query_params.get("short",None)
release_id = request.query_params.get("release_id",None)
isdownload = request.query_params.get("isdownload",None)
password = request.query_params.get("password",None)
udid = request.query_params.get("udid",None)
short = request.query_params.get("short", None)
release_id = request.query_params.get("release_id", None)
isdownload = request.query_params.get("isdownload", None)
password = request.query_params.get("password", None)
udid = request.query_params.get("udid", None)
if not downtoken or not short or not release_id:
res.code=1004
res.msg="参数丢失"
res.code = 1004
res.msg = "参数丢失"
return Response(res.dict)
dtoken = DownloadToken()
if dtoken.verify_token(downtoken,release_id):
app_obj = get_app_instance_by_cache(app_id,password,900,udid)
if dtoken.verify_token(downtoken, release_id):
app_obj = get_app_instance_by_cache(app_id, password, 900, udid)
if app_obj:
if app_obj.get("type") == 0:
apptype = '.apk'
download_url = get_download_url_by_cache(app_obj,release_id + apptype,600)
download_url = get_download_url_by_cache(app_obj, release_id + apptype, 600)
else:
apptype = '.ipa'
if isdownload :
download_url = get_download_url_by_cache(app_obj,release_id + apptype, 600,udid=udid)
if isdownload:
download_url = get_download_url_by_cache(app_obj, release_id + apptype, 600, udid=udid)
else:
download_url = get_download_url_by_cache(app_obj,release_id + apptype,600,isdownload,udid=udid)
download_url = get_download_url_by_cache(app_obj, release_id + apptype, 600, isdownload,
udid=udid)
res.data={"download_url":download_url}
res.data = {"download_url": download_url}
if download_url != "" and not download_url.endswith("mobileconifg"):
set_app_download_by_cache(app_id)
return Response(res.dict)
@ -166,5 +177,3 @@ class InstallView(APIView):
res.code = 1006
res.msg = "该应用不存在"
return Response(res.dict)

@ -5,16 +5,19 @@ from api.utils.serializer import UserInfoSerializer
from django.core.cache import cache
from rest_framework.views import APIView
import binascii
import os,datetime
from api.utils.TokenManager import DownloadToken,generateNumericTokenOfLength
import os, datetime
from api.utils.TokenManager import DownloadToken, generateNumericTokenOfLength
from api.utils.auth import ExpiringTokenAuthentication
from api.utils.response import BaseResponse
from django.middleware import csrf
from fir_ser.settings import CACHE_KEY_TEMPLATE
def get_token(request):
token = csrf.get_token(request)
return {'csrf_token': token}
class LoginView(APIView):
def generate_key(self):
return binascii.hexlify(os.urandom(32)).decode()
@ -32,7 +35,7 @@ class LoginView(APIView):
username = receive.get("username")
password = receive.get("password")
user = auth.authenticate(username=username, password=password)
print(username,password,user)
print(username, password, user)
if user is not None:
if user.is_active:
@ -41,11 +44,11 @@ class LoginView(APIView):
now = datetime.datetime.now()
user_info = UserInfo.objects.get(pk=user.pk)
auth_key = "_".join([CACHE_KEY_TEMPLATE.get('user_auth_token_key'),key])
cache.set(auth_key,{'uid':user_info.uid,'username':user_info.username},3600*24*7)
auth_key = "_".join([CACHE_KEY_TEMPLATE.get('user_auth_token_key'), key])
cache.set(auth_key, {'uid': user_info.uid, 'username': user_info.username}, 3600 * 24 * 7)
Token.objects.create(user=user, **{"access_token": key, "created": now})
serializer = UserInfoSerializer(user_info,)
serializer = UserInfoSerializer(user_info, )
data = serializer.data
response.msg = "验证成功!"
response.userinfo = data
@ -68,43 +71,44 @@ class LoginView(APIView):
return Response(response.dict)
def get(self,request):
def get(self, request):
response = BaseResponse()
csrf=get_token(request)
response.data=csrf
csrf = get_token(request)
response.data = csrf
return Response(response.dict)
class UserInfoView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
def get(self,request):
def get(self, request):
res = BaseResponse()
serializer = UserInfoSerializer(request.user)
res.data = serializer.data
act = request.query_params.get("act",None)
act = request.query_params.get("act", None)
if act and act == "sms":
sms_token_obj=DownloadToken()
sms_code=generateNumericTokenOfLength(6)
sms_token=sms_token_obj.make_token([sms_code])
sms_token_obj = DownloadToken()
sms_code = generateNumericTokenOfLength(6)
sms_token = sms_token_obj.make_token([sms_code])
res.data["sms_token"] = sms_token
res.data["sms_code"] = sms_code
return Response(res.dict)
def put(self,request):
def put(self, request):
res = BaseResponse()
request.user.qq=request.data.get("qq",request.user.qq)
request.user.job=request.data.get("job", request.user.job)
request.user.first_name=request.data.get("first_name", request.user.first_name)
request.user.qq = request.data.get("qq", request.user.qq)
request.user.job = request.data.get("job", request.user.job)
request.user.first_name = request.data.get("first_name", request.user.first_name)
oldpassword=request.data.get("oldpassword",None)
surepassword=request.data.get("surepassword",None)
oldpassword = request.data.get("oldpassword", None)
surepassword = request.data.get("surepassword", None)
if oldpassword and surepassword:
user = auth.authenticate(username=request.user.username, password=oldpassword)
if user is not None:
user.set_password(surepassword)
user.save()
res.msg="密码修改成功"
res.msg = "密码修改成功"
auth_token = request.auth
for token_obj in Token.objects.filter(user=user):
@ -118,15 +122,14 @@ class UserInfoView(APIView):
res.msg = "老密码校验失败"
else:
sms_token = request.data.get("sms_token",None)
sms_token = request.data.get("sms_token", None)
if sms_token:
sms_token_obj=DownloadToken()
if sms_token_obj.verify_token(sms_token,request.data.get("sms_code",None)):
request.user.mobile=request.data.get("mobile", request.user.mobile)
sms_token_obj = DownloadToken()
if sms_token_obj.verify_token(sms_token, request.data.get("sms_code", None)):
request.user.mobile = request.data.get("mobile", request.user.mobile)
request.user.save()
serializer = UserInfoSerializer(request.user)
res.data = serializer.data
return Response(res.dict)
return Response(res.dict)

@ -15,5 +15,5 @@ class LogoutView(APIView):
user = request.user.pk
auth_token = request.auth
cache.delete(auth_token)
Token.objects.filter(user=user,access_token=auth_token).delete()
Token.objects.filter(user=user, access_token=auth_token).delete()
return Response({"code": 1000})

@ -4,23 +4,25 @@
# author: liuyu
# date: 2020/3/6
from api.utils.app.supersignutils import udid_bytes_to_dict,get_redirect_server_domain,IosUtils
from api.models import Apps,AppUDID
from api.utils.app.supersignutils import udid_bytes_to_dict, get_redirect_server_domain, IosUtils
from api.models import Apps, AppUDID
from django.views import View
from django.http import HttpResponsePermanentRedirect,Http404
from django.http import HttpResponsePermanentRedirect, Http404
class IosUDIDView(View):
def post(self,request,short):
def post(self, request, short):
stream_f = str(request.body)
format_udid_info = udid_bytes_to_dict(stream_f)
try:
app_info=Apps.objects.filter(short=short).first()
app_info = Apps.objects.filter(short=short).first()
if app_info :
if app_info:
if app_info.issupersign:
AppUDID.objects.update_or_create(app_id=app_info,**format_udid_info,defaults={'udid':format_udid_info.get('udid')})
ios_obj = IosUtils(format_udid_info,app_info.user_id,app_info)
AppUDID.objects.update_or_create(app_id=app_info, **format_udid_info,
defaults={'udid': format_udid_info.get('udid')})
ios_obj = IosUtils(format_udid_info, app_info.user_id, app_info)
ios_obj.resign()
else:
return Http404
@ -30,6 +32,4 @@ class IosUDIDView(View):
except Exception as e:
print(e)
server_domain = get_redirect_server_domain(request)
return HttpResponsePermanentRedirect("%s/%s?udid=%s"%(server_domain,short,format_udid_info.get("udid")))
return HttpResponsePermanentRedirect("%s/%s?udid=%s" % (server_domain, short, format_udid_info.get("udid")))

@ -10,7 +10,7 @@ from api.utils.auth import ExpiringTokenAuthentication
from rest_framework.response import Response
import json
from api.utils.storage.caches import del_cache_storage
from api.models import AppStorage,UserInfo
from api.models import AppStorage, UserInfo
from api.utils.serializer import StorageSerializer
@ -45,7 +45,7 @@ class StorageView(APIView):
res.storage_list.append({'id': storage_t[0], 'name': storage_t[1]})
admin_storage = UserInfo.objects.filter(is_superuser=True).order_by('pk').first()
res.is_admin_storage=False
res.is_admin_storage = False
if admin_storage.uid == request.user.uid:
res.is_admin_storage = True
return Response(res.dict)
@ -72,7 +72,7 @@ class StorageView(APIView):
res = BaseResponse()
data = request.data
use_storage_id=data.get("use_storage_id",None)
use_storage_id = data.get("use_storage_id", None)
if use_storage_id:
try:
if use_storage_id == -1:
@ -82,8 +82,8 @@ class StorageView(APIView):
del_cache_storage(request.user)
except Exception as e:
print(e)
res.code=1006
res.msg='修改失败'
res.code = 1006
res.msg = '修改失败'
return Response(res.dict)
try:

@ -8,15 +8,14 @@ from rest_framework.views import APIView
from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication
from rest_framework.response import Response
from api.models import AppIOSDeveloperInfo,APPSuperSignUsedInfo,AppUDID
from api.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer,DeviceUDIDSerializer
from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID
from api.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer
from rest_framework.pagination import PageNumberPagination
from api.utils.app.supersignutils import IosUtils
from api.utils.utils import get_developer_devices
from api.utils.storage.caches import developer_auth_code
class AppsPageNumber(PageNumberPagination):
page_size = 10 # 每页显示多少条
page_size_query_param = 'size' # URL中每页显示条数的参数
@ -33,45 +32,54 @@ class DeveloperView(APIView):
appid = request.query_params.get("appid", None)
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user)
res.use_num=get_developer_devices(developer_obj)
res.use_num = get_developer_devices(developer_obj)
if appid:
developer_obj=developer_obj.filter(email=appid)
developer_obj = developer_obj.filter(email=appid)
page_obj = AppsPageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=developer_obj.order_by("-updated_time"), request=request,
app_page_serializer = page_obj.paginate_queryset(queryset=developer_obj.order_by("-updated_time"),
request=request,
view=self)
Developer_serializer = DeveloperSerializer(app_page_serializer, many=True,)
Developer_serializer = DeveloperSerializer(app_page_serializer, many=True, )
res.data = Developer_serializer.data
res.count=developer_obj.count()
res.count = developer_obj.count()
return Response(res.dict)
def put(self,request):
def put(self, request):
data = request.data
email=data.get("email",None)
email = data.get("email", None)
if email:
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user,email=email).first()
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, email=email).first()
if developer_obj:
act = data.get("act", None)
if act:
res = BaseResponse()
if act == "preactive":
developer_auth_code("del", request.user, developer_obj.email)
status,result = IosUtils.active_developer(developer_obj,request.user)
status, result = IosUtils.active_developer(developer_obj, request.user)
if status:
if not developer_obj.certid:
status, result = IosUtils.create_developer_cert(developer_obj, request.user)
if status:
IosUtils.get_device_from_developer(developer_obj, request.user)
else:
res.code = 1008
res.msg = result.get("err_info")
return Response(res.dict)
return self.get(request)
else:
res.code = 1008
res.msg = result.get("return_info")
return Response(res.dict)
elif act == "nowactive" :
elif act == "nowactive":
code = data.get("code", None)
if code:
developer_auth_code("set",request.user,developer_obj.email,code)
developer_auth_code("set", request.user, developer_obj.email, code)
elif act == "ioscert":
if not developer_obj.certid:
status,result=IosUtils.create_developer_cert(developer_obj, request.user)
status, result = IosUtils.create_developer_cert(developer_obj, request.user)
if status:
IosUtils.get_device_from_developer(developer_obj, request.user)
else:
@ -79,11 +87,25 @@ class DeveloperView(APIView):
res.msg = result.get("err_info")
return Response(res.dict)
elif act == "syncdevice":
IosUtils.get_device_from_developer(developer_obj,request.user)
status, result = IosUtils.get_device_from_developer(developer_obj, request.user)
if status:
IosUtils.get_device_from_developer(developer_obj, request.user)
else:
res.code = 1008
res.msg = result.get("err_info")
return Response(res.dict)
elif act == "checkauth":
developer_auth_code("del", request.user, developer_obj.email)
status, result = IosUtils.active_developer(developer_obj, request.user)
if status:
return self.get(request)
else:
res.code = 1008
res.msg = result.get("return_info")
return Response(res.dict)
else:
try:
usable_number=int(data.get("usable_number",developer_obj.usable_number))
usable_number = int(data.get("usable_number", developer_obj.usable_number))
if usable_number >= 0 and usable_number <= 100:
developer_obj.usable_number = usable_number
except Exception as e:
@ -91,41 +113,42 @@ class DeveloperView(APIView):
developer_obj.description = data.get("description", developer_obj.description)
password = data.get("password", developer_obj.password)
if password != "" and password != developer_obj.password:
developer_obj.password=password
developer_obj.is_actived=False
developer_obj.password = password
developer_obj.is_actived = False
developer_obj.save()
return self.get(request)
def post(self,request):
def post(self, request):
data = request.data
datainfo={
"usable_number" : data.get("usable_number", ""),
"description" : data.get("description", ""),
"password" : data.get("password", ""),
"email" : data.get("email", ""),
datainfo = {
"usable_number": data.get("usable_number", ""),
"description": data.get("description", ""),
"password": data.get("password", ""),
"email": data.get("email", ""),
}
try:
AppIOSDeveloperInfo.objects.create(user_id=request.user,**datainfo)
AppIOSDeveloperInfo.objects.create(user_id=request.user, **datainfo)
except Exception as e:
print(e)
res = BaseResponse()
res.code=1005
res.msg="添加失败"
res.code = 1005
res.msg = "添加失败"
return Response(res.dict)
return self.get(request)
def delete(self,request):
email=request.query_params.get("email",None)
def delete(self, request):
email = request.query_params.get("email", None)
if email:
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user,email=email).first()
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, email=email).first()
if developer_obj:
IosUtils.clean_developer(developer_obj,request.user)
IosUtils.clean_developer(developer_obj, request.user)
developer_obj.delete()
return self.get(request)
class SuperSignUsedView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
@ -136,23 +159,25 @@ class SuperSignUsedView(APIView):
bundleid = request.query_params.get("bundleid", None)
developerid = request.query_params.get("appid", None)
SuperSignUsed_obj = APPSuperSignUsedInfo.objects.filter(user_id=request.user,)
SuperSignUsed_obj = APPSuperSignUsedInfo.objects.filter(user_id=request.user, )
if developerid:
SuperSignUsed_obj=SuperSignUsed_obj.filter(developerid__email=developerid)
SuperSignUsed_obj = SuperSignUsed_obj.filter(developerid__email=developerid)
if udid:
SuperSignUsed_obj = SuperSignUsed_obj.filter(udid__udid=udid)
if bundleid:
SuperSignUsed_obj = SuperSignUsed_obj.filter(app_id__bundle_id=bundleid)
page_obj = AppsPageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=SuperSignUsed_obj.order_by("-created_time"), request=request,
app_page_serializer = page_obj.paginate_queryset(queryset=SuperSignUsed_obj.order_by("-created_time"),
request=request,
view=self)
app_serializer = SuperSignUsedSerializer(app_page_serializer, many=True, )
res.data = app_serializer.data
res.count=SuperSignUsed_obj.count()
res.count = SuperSignUsed_obj.count()
return Response(res.dict)
class AppUDIDUsedView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
@ -168,18 +193,19 @@ class AppUDIDUsedView(APIView):
AppUDID_obj = AppUDID_obj.filter(app_id__bundle_id=bundleid)
page_obj = AppsPageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=AppUDID_obj.order_by("-created_time"), request=request,
app_page_serializer = page_obj.paginate_queryset(queryset=AppUDID_obj.order_by("-created_time"),
request=request,
view=self)
app_serializer = DeviceUDIDSerializer(app_page_serializer, many=True,)
app_serializer = DeviceUDIDSerializer(app_page_serializer, many=True, )
res.data = app_serializer.data
res.count=AppUDID_obj.count()
res.count = AppUDID_obj.count()
return Response(res.dict)
def delete(self,request):
def delete(self, request):
res = BaseResponse()
id = request.query_params.get("id", None)
app_id = request.query_params.get("aid", None)
app_udid_obj=AppUDID.objects.filter(app_id__user_id_id=request.user,pk=id)
IosUtils.disable_udid(app_udid_obj.first(),app_id)
app_udid_obj = AppUDID.objects.filter(app_id__user_id_id=request.user, pk=id)
IosUtils.disable_udid(app_udid_obj.first(), app_id)
app_udid_obj.delete()
return Response(res.dict)

@ -4,10 +4,10 @@
# author: liuyu
# date: 2020/3/6
from api.utils.app.apputils import get_random_short,SaveAppInfos
from api.utils.app.apputils import get_random_short, SaveAppInfos
from api.utils.storage.storage import Storage
from api.utils.storage.caches import del_cache_response_by_short
from api.models import Apps,AppReleaseInfo
from api.models import Apps, AppReleaseInfo
from api.utils.app.randomstrings import make_app_uuid
from rest_framework.views import APIView
from api.utils.response import BaseResponse
@ -16,10 +16,10 @@ from api.utils.app.randomstrings import make_from_user_uuid
from rest_framework.response import Response
from fir_ser import settings
from api.utils.TokenManager import DownloadToken
import os,json
import os, json
class AppAnalyseView(APIView):
class AppAnalyseView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
def post(self, request):
@ -31,61 +31,61 @@ class AppAnalyseView(APIView):
if bundleid and app_type:
ap = 'apk'
if app_type == 'iOS':
ap='ipa'
ap = 'ipa'
app_uuid = make_app_uuid(request.user, bundleid + ap)
release_id = make_from_user_uuid(request.user)
png_id = make_from_user_uuid(request.user)
app_obj = Apps.objects.filter(app_id=app_uuid).first()
binary_url=''
binary_url = ''
if app_obj:
is_new=False
is_new = False
short = app_obj.short
app_release_obj=AppReleaseInfo.objects.filter(app_id=app_obj,is_master=True).first()
app_release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
if app_release_obj:
binary_url = app_release_obj.binary_url
else:
is_new=True
is_new = True
short = get_random_short()
if app_type == 'iOS':
upload_key = release_id+'.ipa'
upload_key = release_id + '.ipa'
else:
upload_key = release_id+'.apk'
png_key = png_id+'.png'
upload_key = release_id + '.apk'
png_key = png_id + '.png'
storage = Storage(request.user)
storage_type =storage.get_storage_type()
storage_type = storage.get_storage_type()
if storage_type == 1:
upload_token = storage.get_upload_token(upload_key)
png_token = storage.get_upload_token(png_key)
elif storage_type == 2:
png_token=upload_token = storage.get_upload_token(upload_key)
png_token = upload_token = storage.get_upload_token(upload_key)
else:
upload_token = storage.get_upload_token(upload_key)
png_token = storage.get_upload_token(png_key)
res.data = {"app_uuid": app_uuid, "short": short,
"domain_name": request.user.domain_name,
"upload_token":upload_token,
"upload_key":upload_key,
"png_token":png_token,
"png_key":png_key,
"storage":storage_type,
"is_new":is_new,"binary_url":binary_url}
"upload_token": upload_token,
"upload_key": upload_key,
"png_token": png_token,
"png_key": png_key,
"storage": storage_type,
"is_new": is_new, "binary_url": binary_url}
else:
res.code = 1003
return Response(res.dict)
def put(self,request):
def put(self, request):
res = BaseResponse()
data=request.data
appinfo={
"labelname":data.get("appname"),
"version":data.get("buildversion"),
"versioncode":data.get("version"),
"release_type":data.get("release_type"),
"miniOSversion":data.get("miniosversion"),
"changelog":data.get("changelog",''),
data = request.data
appinfo = {
"labelname": data.get("appname"),
"version": data.get("buildversion"),
"versioncode": data.get("version"),
"release_type": data.get("release_type"),
"miniOSversion": data.get("miniosversion"),
"changelog": data.get("changelog", ''),
"udid": data.get("udid", ''),
}
@ -104,14 +104,14 @@ class UploadView(APIView):
'''
authentication_classes = [ExpiringTokenAuthentication, ]
def get(self,request):
def get(self, request):
res = BaseResponse()
storage = Storage(request.user)
request_upload_key=request.query_params.get("upload_key",None)
app_id=request.query_params.get("app_id",None)
ftype = request.query_params.get("ftype",None)
if ftype and app_id and request_upload_key :
if ftype =='app':
request_upload_key = request.query_params.get("upload_key", None)
app_id = request.query_params.get("app_id", None)
ftype = request.query_params.get("ftype", None)
if ftype and app_id and request_upload_key:
if ftype == 'app':
app_obj = Apps.objects.filter(app_id=app_id, user_id=request.user).first()
elif ftype and ftype == 'head':
@ -120,25 +120,25 @@ class UploadView(APIView):
res.msg = '该用户不存在'
return Response(res.dict)
else:
app_obj=True
app_obj = True
else:
app_obj=False
app_obj = False
if app_obj:
app_type = request_upload_key.split(".")[-1]
if app_type not in ["apk", "ipa", 'png', 'jpeg', 'jpg']:
res.code = 1006
res.msg='类型不允许'
res.msg = '类型不允许'
else:
upload_key = make_from_user_uuid(request.user)+'.'+app_type
upload_key = make_from_user_uuid(request.user) + '.' + app_type
upload_token = storage.get_upload_token(upload_key)
storage_type = storage.get_storage_type()
res.data = {"domain_name": request.user.domain_name,
"upload_token": upload_token,
"upload_key": upload_key,
"storage": storage_type,
"app_id":app_id,
"ftype":ftype}
"app_id": app_id,
"ftype": ftype}
else:
res.code = 1006
res.msg = '该应用不存在'
@ -146,8 +146,6 @@ class UploadView(APIView):
res.code = 1006
return Response(res.dict)
def post(self, request):
res = BaseResponse()
@ -168,10 +166,11 @@ class UploadView(APIView):
return Response(res.dict)
token_obj = DownloadToken()
if token_obj.verify_token(token=certinfo.get("upload_token",None),release_id=certinfo.get("upload_key",None)):
if token_obj.verify_token(token=certinfo.get("upload_token", None),
release_id=certinfo.get("upload_key", None)):
app_id = certinfo.get("app_id", None)
ftype = certinfo.get("ftype",None)
ftype = certinfo.get("ftype", None)
if ftype and ftype == 'app':
pass
# app_obj = Apps.objects.filter(app_id=app_id, user_id=request.user).first()
@ -189,11 +188,11 @@ class UploadView(APIView):
res.msg = '该请求不存在'
return Response(res.dict)
if not files:
res.msg="文件不存在"
res.msg = "文件不存在"
for file_obj in files:
try:
app_type = file_obj.name.split(".")[-1]
if app_type not in ["apk","ipa",'png','jpeg','jpg']:
if app_type not in ["apk", "ipa", 'png', 'jpeg', 'jpg']:
raise TypeError
except Exception as e:
print(e)
@ -202,7 +201,7 @@ class UploadView(APIView):
return Response(res.dict)
random_file_name = make_from_user_uuid(request.user)
local_file = os.path.join(settings.MEDIA_ROOT,certinfo.get("upload_key",random_file_name))
local_file = os.path.join(settings.MEDIA_ROOT, certinfo.get("upload_key", random_file_name))
# 读取传入的文件
try:
destination = open(local_file, 'wb+')
@ -224,23 +223,23 @@ class UploadView(APIView):
return Response(res.dict)
def put(self,request):
def put(self, request):
res = BaseResponse()
certinfo = request.data.get('certinfo',None)
certinfo = request.data.get('certinfo', None)
if certinfo:
app_id = certinfo.get("app_id", None)
ftype = certinfo.get('ftype',None)
ftype = certinfo.get('ftype', None)
storage = Storage(request.user)
if ftype and ftype == 'app':
app_obj = Apps.objects.filter(app_id=app_id, user_id=request.user).first()
if app_obj:
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj,is_master=True).first()
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
if release_obj:
old_file_key = release_obj.icon_url
release_obj.icon_url = certinfo.get("upload_key")
release_obj.save()
del_cache_response_by_short(app_obj.short,app_id)
del_cache_response_by_short(app_obj.short, app_id)
storage.delete_file(old_file_key)
return Response(res.dict)
elif ftype and ftype == 'head':
@ -259,7 +258,3 @@ class UploadView(APIView):
pass
res.code = 1008
return Response(res.dict)

@ -15,7 +15,6 @@ import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
@ -27,7 +26,6 @@ DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
@ -38,7 +36,7 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'api.apps.ApiConfig',
'django_apscheduler',#定时执行任务
'django_apscheduler', # 定时执行任务
'rest_framework',
]
@ -46,7 +44,7 @@ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
@ -74,7 +72,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'fir_ser.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
@ -90,13 +87,12 @@ DATABASES = {
'PASSWORD': 'KGzKjZpWBp4R4RSa',
'HOST': '127.0.0.1',
'PORT': '3306',
#设置MySQL的驱动
# 设置MySQL的驱动
'OPTIONS': {'init_command': 'SET storage_engine=INNODB'}
}
}
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
@ -133,24 +129,21 @@ USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS=[
os.path.join(BASE_DIR,"static")
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static")
]
# Media配置
MEDIA_URL = "files/"
MEDIA_ROOT = os.path.join(BASE_DIR, "files")
#supersign配置
# supersign配置
SUPER_SIGN_ROOT = os.path.join(BASE_DIR, "supersign")
AUTH_USER_MODEL="api.UserInfo"
AUTH_USER_MODEL = "api.UserInfo"
GEE_TEST = {
"gee_test_access_id": "37ca5631edd1e882721808d35163b3ad",
@ -161,8 +154,6 @@ GEE_TEST = {
], # 不用验证的用户(存放着用户的uid)
}
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
@ -171,12 +162,11 @@ CACHES = {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100},
"PASSWORD": "nineven",
"DECODE_RESPONSES":True
"DECODE_RESPONSES": True
}
},
}
# DRF扩展缓存时间
REST_FRAMEWORK_EXTENSIONS = {
# 缓存时间
@ -200,10 +190,10 @@ THIRD_PART_CONFIG = {
'name': 'local',
'type': 0,
'auth': {
'domain_name': '127.0.0.1:8000', # 正式环境需要填写正式的访问域名
'is_https':False,
'domain_name': 'fly.harmonygames.cn', # 正式环境需要填写正式的访问域名
'is_https': True,
},
'active': False
'active': True
},
{
'name': 'aliyun',
@ -212,12 +202,12 @@ THIRD_PART_CONFIG = {
'access_key': 'LTAI4FkbTRqejBAzB1yQZpjR',
'secret_key': '2WzCsKjYXxOvU4wPtTmrrXnxiLIxy9',
'bucket_name': 'fly-app-storage',
'sts_role_arn':'acs:ram::1886562303345632:role/fly-app-storage',
'endpoint':'oss-cn-beijing.aliyuncs.com',
'is_https':True,
'domain_name':'ali.storage.dvcloud.xin'
'sts_role_arn': 'acs:ram::1886562303345632:role/fly-app-storage',
'endpoint': 'oss-cn-beijing.aliyuncs.com',
'is_https': True,
'domain_name': 'ali.storage.dvcloud.xin'
},
'active': True
'active': False
},
{
'name': 'qiniuyun',
@ -234,28 +224,28 @@ THIRD_PART_CONFIG = {
]
}
CACHE_KEY_TEMPLATE={
'download_times_key':'app_download_times',
'make_token_key':'make_token',
'download_short_key':'download_short',
'app_instance_key':'app_instance',
'download_url_key':'download_url',
'user_storage_key':'storage_auth',
'user_auth_token_key':'user_auth_token',
'download_today_times_key':'download_today_times',
CACHE_KEY_TEMPLATE = {
'download_times_key': 'app_download_times',
'make_token_key': 'make_token',
'download_short_key': 'download_short',
'app_instance_key': 'app_instance',
'download_url_key': 'download_url',
'user_storage_key': 'storage_auth',
'user_auth_token_key': 'user_auth_token',
'download_today_times_key': 'download_today_times',
'developer_auth_code_key': 'developer_auth_code',
}
SYNC_CACHE_TO_DATABASE={
'download_times':10 #下载次数同步时间
SYNC_CACHE_TO_DATABASE = {
'download_times': 10 # 下载次数同步时间
}
SERVER_DOMAIN={
'IOS_PMFILE_DOWNLOAD_DOMAIN':{
"domain_name":'fly.harmonygames.cn',
'is_https':True,
SERVER_DOMAIN = {
'IOS_PMFILE_DOWNLOAD_DOMAIN': {
"domain_name": 'fly.harmonygames.cn',
'is_https': True,
}, # ios 描述文件和plist文件下载域名,该域名用于后端,一般为api访问域名
'POST_UDID_DOMAIN':'https://fly.harmonygames.cn', #超级签名 安装签名时 向该域名 发送udid数据,该域名用于后端,一般为 api 访问域名
'POST_UDID_DOMAIN': 'https://fly.harmonygames.cn', # 超级签名 安装签名时 向该域名 发送udid数据,该域名用于后端,一般为 api 访问域名
'REDIRECT_UDID_DOMAIN': 'https://fly.harmonygames.cn', # 超级签名 安装完成之后,跳转域名,该域名为前端web访问域名
}

@ -14,7 +14,7 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,re_path,include
from django.urls import path, re_path, include
from rest_framework.routers import DefaultRouter
# from api.views.course import CourseView,CourseDetailView,CourseCategoryView
from api.views.login import LoginView
@ -32,10 +32,9 @@ from api.views.captcha import CaptchaView
# router.register("course/category", CourseCategoryView)
from django.views.static import serve
from fir_ser import settings
from api.views.download import DownloadView,InstallView
from api.views.download import DownloadView, InstallView
from api.views.receiveudids import IosUDIDView
urlpatterns = [
path('fly.admin/', admin.site.urls),
# path("",include(router.urls)),
@ -49,7 +48,7 @@ urlpatterns = [
# re_path("api/v1/trade/alipay/",AlipayTradeView),
# media路径配置
re_path(r'files/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
re_path("download/(?P<filename>\w+\.\w+)$", DownloadView.as_view(),name="download"),
re_path("download/(?P<filename>\w+\.\w+)$", DownloadView.as_view(), name="download"),
re_path("install/(?P<app_id>\w+)$", InstallView.as_view(), name="install"),
re_path("^udid/(?P<short>\w+)$", IosUDIDView.as_view()),
]

@ -1,9 +1,9 @@
import requests
import random
short=["bgql","wjbu","vnfg","bgql"]
udid_lists=[
short = ["bgql", "wjbu", "vnfg", "bgql"]
udid_lists = [
"f55df38afe5c1242b8bc478d0182bbd0d7d08929",
"f55df38afe5c1242b8bc478d0182bbd0d7d08929",
"f55df38afe5c1242b8bc478d0182bbd0d7d08922",
@ -34,12 +34,10 @@ udid_lists=[
"f55df38afe5c1242b8bc478d0182bbd0d7dsfe08929",
]
for udid in udid_lists:
data='<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n\t<key>PRODUCT</key>\n\t<string>iPhone9,1</string>\n\t<key>SERIAL</key>\n\t<string>F71YD58GHG74</string>\n\t<key>UDID</key>\n\t<string>%s</string>\n\t<key>VERSION</key>\n\t<string>17E262</string>\n</dict>\n</plist>\n' %(udid)
data = '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n\t<key>PRODUCT</key>\n\t<string>iPhone9,1</string>\n\t<key>SERIAL</key>\n\t<string>F71YD58GHG74</string>\n\t<key>UDID</key>\n\t<string>%s</string>\n\t<key>VERSION</key>\n\t<string>17E262</string>\n</dict>\n</plist>\n' % (
udid)
uri = "https://fly.harmonygames.cn/udid/%s" % (random.choice(short))
# uri = "https://fly.harmonygames.cn/udid/%s" % ("bgql")
req=requests.post(uri,data=data )
req = requests.post(uri, data=data)
print(req.status_code)
Loading…
Cancel
Save