two_factor_modal.html 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. {{define "modals/twoFactorModal"}}
  2. <a-modal id="two-factor-modal" v-model="twoFactorModal.visible" :title="twoFactorModal.title" :closable="true"
  3. :class="themeSwitcher.currentTheme">
  4. <template v-if="twoFactorModal.type === 'set'">
  5. <p>{{ i18n "pages.settings.security.twoFactorModalSteps" }}</p>
  6. <a-divider></a-divider>
  7. <p>{{ i18n "pages.settings.security.twoFactorModalFirstStep" }}</p>
  8. <div :style="{ display: 'flex', alignItems: 'center', flexDirection: 'column', gap: '12px' }">
  9. <div
  10. :style="{ border: '1px solid', borderRadius: '1rem', borderColor: themeSwitcher.isDarkTheme ? 'var(--dark-color-surface-300)' : '#d9d9d9', padding: 0 }">
  11. <img :src="twoFactorModal.qrImage"
  12. :style="{ filter: themeSwitcher.isDarkTheme ? 'invert(1)' : 'none'}"
  13. :alt="twoFactorModal.token">
  14. </div>
  15. <span :style="{ fontSize: '12px', fontFamily: 'monospace' }">[[ twoFactorModal.token ]]</span>
  16. </div>
  17. <a-divider></a-divider>
  18. <p>{{ i18n "pages.settings.security.twoFactorModalSecondStep" }}</p>
  19. <a-input v-model.trim="twoFactorModal.enteredCode" :style="{ width: '100%' }"></a-input>
  20. </template>
  21. <template v-if="twoFactorModal.type === 'remove'">
  22. <p>{{ i18n "pages.settings.security.twoFactorModalRemoveStep" }}</p>
  23. <a-input v-model.trim="twoFactorModal.enteredCode" :style="{ width: '100%' }"></a-input>
  24. </template>
  25. <template slot="footer">
  26. <a-button @click="twoFactorModal.cancel">
  27. <span>{{ i18n "cancel" }}</span>
  28. </a-button>
  29. <a-button type="primary" :disabled="twoFactorModal.enteredCode.length < 6" @click="twoFactorModal.ok">
  30. <span>{{ i18n "confirm" }}</span>
  31. </a-button>
  32. </template>
  33. </a-modal>
  34. <script>
  35. const twoFactorModal = {
  36. title: '',
  37. fileName: '',
  38. token: '',
  39. enteredCode: '',
  40. visible: false,
  41. type: 'set',
  42. confirm: null,
  43. totpObject: null,
  44. qrImage: "",
  45. ok() {
  46. if (twoFactorModal.totpObject.generate() === twoFactorModal.enteredCode) {
  47. ObjectUtil.execute(twoFactorModal.confirm, true)
  48. twoFactorModal.close()
  49. switch (twoFactorModal.type) {
  50. case 'set':
  51. Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalSetSuccess" }}')
  52. break;
  53. case 'remove':
  54. Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalDeleteSuccess" }}')
  55. break;
  56. default:
  57. break;
  58. }
  59. } else {
  60. Vue.prototype.$message['error']('{{ i18n "pages.settings.security.twoFactorModalError" }}')
  61. }
  62. },
  63. cancel() {
  64. ObjectUtil.execute(twoFactorModal.confirm, false)
  65. twoFactorModal.close()
  66. },
  67. show: function ({
  68. title = '',
  69. token = '',
  70. type = 'set',
  71. confirm = (success) => { }
  72. }) {
  73. this.title = title;
  74. this.token = token;
  75. this.visible = true;
  76. this.confirm = confirm;
  77. this.type = type;
  78. this.totpObject = new OTPAuth.TOTP({
  79. issuer: "3x-ui",
  80. label: "Administrator",
  81. algorithm: "SHA1",
  82. digits: 6,
  83. period: 30,
  84. secret: twoFactorModal.token,
  85. });
  86. if (type === 'set') {
  87. this.qrImage = new QRious({
  88. size: 150,
  89. value: twoFactorModal.totpObject.toString(),
  90. background: 'white',
  91. backgroundAlpha: 0,
  92. foreground: 'black',
  93. padding: 12,
  94. level: 'L'
  95. }).toDataURL()
  96. }
  97. },
  98. close: function () {
  99. twoFactorModal.enteredCode = "";
  100. twoFactorModal.visible = false;
  101. },
  102. };
  103. const twoFactorModalApp = new Vue({
  104. delimiters: ['[[', ']]'],
  105. el: '#two-factor-modal',
  106. data: {
  107. twoFactorModal: twoFactorModal,
  108. },
  109. });
  110. </script>
  111. {{end}}