1
0

two_factor_modal.html 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  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 class="qr-bg" :style="{ width: '180px', height: '180px' }">
  10. <canvas @click="copy(twoFactorModal.token)" id="twofactor-qrcode" class="qr-cv"></canvas>
  11. </div>
  12. <span :style="{ fontSize: '12px', fontFamily: 'monospace' }">[[ twoFactorModal.token ]]</span>
  13. </div>
  14. <a-divider></a-divider>
  15. <p>{{ i18n "pages.settings.security.twoFactorModalSecondStep" }}</p>
  16. <a-input v-model.trim="twoFactorModal.enteredCode" :style="{ width: '100%' }"></a-input>
  17. </template>
  18. <template v-if="twoFactorModal.type === 'confirm'">
  19. <p>[[ twoFactorModal.description ]]</p>
  20. <a-input v-model.trim="twoFactorModal.enteredCode" :style="{ width: '100%' }"></a-input>
  21. </template>
  22. <template slot="footer">
  23. <a-button @click="twoFactorModal.cancel">
  24. <span>{{ i18n "cancel" }}</span>
  25. </a-button>
  26. <a-button type="primary" :disabled="twoFactorModal.enteredCode.length < 6" @click="twoFactorModal.ok">
  27. <span>{{ i18n "confirm" }}</span>
  28. </a-button>
  29. </template>
  30. </a-modal>
  31. <script>
  32. const twoFactorModal = {
  33. title: '',
  34. description: '',
  35. fileName: '',
  36. token: '',
  37. enteredCode: '',
  38. visible: false,
  39. type: 'set',
  40. confirm: null,
  41. totpObject: null,
  42. qrImage: "",
  43. ok() {
  44. if (twoFactorModal.totpObject.generate() === twoFactorModal.enteredCode) {
  45. ObjectUtil.execute(twoFactorModal.confirm, true)
  46. twoFactorModal.close()
  47. } else {
  48. Vue.prototype.$message['error']('{{ i18n "pages.settings.security.twoFactorModalError" }}')
  49. }
  50. },
  51. cancel() {
  52. ObjectUtil.execute(twoFactorModal.confirm, false)
  53. twoFactorModal.close()
  54. },
  55. show: function ({
  56. title = '',
  57. description = '',
  58. token = '',
  59. type = 'set',
  60. confirm = (success) => { }
  61. }) {
  62. this.title = title;
  63. this.description = description;
  64. this.token = token;
  65. this.visible = true;
  66. this.confirm = confirm;
  67. this.type = type;
  68. this.totpObject = new OTPAuth.TOTP({
  69. issuer: "3x-ui",
  70. label: "Administrator",
  71. algorithm: "SHA1",
  72. digits: 6,
  73. period: 30,
  74. secret: twoFactorModal.token,
  75. });
  76. },
  77. close: function () {
  78. twoFactorModal.enteredCode = "";
  79. twoFactorModal.visible = false;
  80. },
  81. };
  82. const twoFactorModalApp = new Vue({
  83. delimiters: ['[[', ']]'],
  84. el: '#two-factor-modal',
  85. data: {
  86. twoFactorModal: twoFactorModal,
  87. },
  88. updated() {
  89. if (
  90. this.twoFactorModal.visible &&
  91. this.twoFactorModal.type === 'set' &&
  92. document.getElementById('twofactor-qrcode')
  93. ) {
  94. this.setQrCode('twofactor-qrcode', this.twoFactorModal.totpObject.toString());
  95. }
  96. },
  97. methods: {
  98. setQrCode(elementId, content) {
  99. new QRious({
  100. element: document.getElementById(elementId),
  101. size: 200,
  102. value: content,
  103. background: 'white',
  104. backgroundAlpha: 0,
  105. foreground: 'black',
  106. padding: 2,
  107. level: 'L'
  108. });
  109. },
  110. copy(content) {
  111. ClipboardManager
  112. .copyText(content)
  113. .then(() => {
  114. app.$message.success('{{ i18n "copied" }}')
  115. })
  116. },
  117. }
  118. });
  119. </script>
  120. {{end}}