Recipients.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. <template>
  2. <div>
  3. <div class="mb-6 flex flex-col md:flex-row justify-between md:items-center">
  4. <div class="relative">
  5. <input
  6. v-model="search"
  7. @keyup.esc="search = ''"
  8. tabindex="0"
  9. type="text"
  10. class="w-full md:w-64 appearance-none shadow bg-white text-grey-700 focus:outline-none rounded py-3 pl-3 pr-8"
  11. placeholder="Search Recipients"
  12. />
  13. <icon
  14. v-if="search"
  15. @click.native="search = ''"
  16. name="close-circle"
  17. class="absolute right-0 inset-y-0 w-5 h-full text-grey-300 fill-current mr-2 flex items-center cursor-pointer"
  18. />
  19. <icon
  20. v-else
  21. name="search"
  22. class="absolute right-0 inset-y-0 w-5 h-full text-grey-300 fill-current pointer-events-none mr-2 flex items-center"
  23. />
  24. </div>
  25. <div class="mt-4 md:mt-0">
  26. <button
  27. @click="addRecipientModalOpen = true"
  28. class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:outline-none ml-auto"
  29. >
  30. Add Recipient
  31. </button>
  32. </div>
  33. </div>
  34. <vue-good-table
  35. @on-search="debounceToolips"
  36. :columns="columns"
  37. :rows="rows"
  38. :search-options="{
  39. enabled: true,
  40. skipDiacritics: true,
  41. externalQuery: search,
  42. }"
  43. :sort-options="{
  44. enabled: true,
  45. initialSortBy: { field: 'created_at', type: 'desc' },
  46. }"
  47. styleClass="vgt-table"
  48. >
  49. <div slot="emptystate" class="flex items-center justify-center h-24 text-lg text-grey-700">
  50. No recipients found for that search!
  51. </div>
  52. <template slot="table-column" slot-scope="props">
  53. <span v-if="props.column.label == 'Key'">
  54. Key
  55. <span
  56. class="tooltip outline-none"
  57. :data-tippy-content="`Use this to attach recipients to new aliases as they are created e.g. alias+key@${user.username}.anonaddy.com. You can attach multiple recipients by doing alias+2.3.4@${user.username}.anonaddy.com. Separating each key by a full stop.`"
  58. >
  59. <icon name="info" class="inline-block w-4 h-4 text-grey-300 fill-current" />
  60. </span>
  61. </span>
  62. <span v-else>
  63. {{ props.column.label }}
  64. </span>
  65. </template>
  66. <template slot="table-row" slot-scope="props">
  67. <span
  68. v-if="props.column.field == 'created_at'"
  69. class="tooltip outline-none text-sm"
  70. :data-tippy-content="rows[props.row.originalIndex].created_at | formatDate"
  71. >{{ props.row.created_at | timeAgo }}
  72. </span>
  73. <span v-else-if="props.column.field == 'key'">
  74. {{ props.row.key }}
  75. </span>
  76. <span v-else-if="props.column.field == 'email'">
  77. <span
  78. class="tooltip cursor-pointer outline-none"
  79. data-tippy-content="Click to copy"
  80. v-clipboard="() => rows[props.row.originalIndex].email"
  81. v-clipboard:success="clipboardSuccess"
  82. v-clipboard:error="clipboardError"
  83. >{{ props.row.email | truncate(30) }}</span
  84. >
  85. <span
  86. v-if="isDefault(props.row.id)"
  87. class="ml-3 py-1 px-2 text-sm bg-yellow-200 text-yellow-900 rounded-full tooltip"
  88. data-tippy-content="The default recipient will be used for all aliases with no other recipients assigned"
  89. >
  90. default
  91. </span>
  92. </span>
  93. <span v-else-if="props.column.field === 'aliases'">
  94. <span
  95. v-if="props.row.aliases.length"
  96. class="tooltip outline-none"
  97. :data-tippy-content="aliasesTooltip(props.row.aliases, isDefault(props.row.id))"
  98. >{{ props.row.aliases[0].email | truncate(40) }}
  99. <span
  100. v-if="isDefault(props.row.id) && aliasesUsingDefaultCount > 1"
  101. class="block text-grey-500 text-sm"
  102. >
  103. + {{ aliasesUsingDefaultCount - 1 }}</span
  104. >
  105. <span v-else-if="props.row.aliases.length > 1" class="block text-grey-500 text-sm">
  106. + {{ props.row.aliases.length - 1 }}</span
  107. >
  108. </span>
  109. <span v-else class="block text-grey-500 text-sm">{{ props.row.aliases.length }}</span>
  110. </span>
  111. <span
  112. v-else-if="props.column.field === 'can_reply_send'"
  113. class="flex justify-center items-center"
  114. >
  115. <Toggle
  116. v-model="rows[props.row.originalIndex].can_reply_send"
  117. @on="allowRepliesSends(props.row.id)"
  118. @off="disallowRepliesSends(props.row.id)"
  119. />
  120. </span>
  121. <span v-else-if="props.column.field === 'should_encrypt'">
  122. <span v-if="props.row.fingerprint" class="flex">
  123. <Toggle
  124. v-model="rows[props.row.originalIndex].should_encrypt"
  125. @on="turnOnEncryption(props.row.id)"
  126. @off="turnOffEncryption(props.row.id)"
  127. />
  128. <icon
  129. name="fingerprint"
  130. class="tooltip outline-none cursor-pointer block w-6 h-6 text-grey-300 fill-current mx-2"
  131. :data-tippy-content="props.row.fingerprint"
  132. v-clipboard="() => props.row.fingerprint"
  133. v-clipboard:success="clipboardSuccess"
  134. v-clipboard:error="clipboardError"
  135. />
  136. <icon
  137. name="delete"
  138. class="tooltip outline-none cursor-pointer block w-6 h-6 text-grey-300 fill-current"
  139. @click.native="openDeleteRecipientKeyModal(props.row)"
  140. data-tippy-content="Remove public key"
  141. />
  142. </span>
  143. <button
  144. v-else
  145. @click="openRecipientKeyModal(props.row)"
  146. class="focus:outline-none text-sm"
  147. >
  148. Add public key
  149. </button>
  150. </span>
  151. <span v-else-if="props.column.field === 'email_verified_at'">
  152. <span
  153. name="check"
  154. v-if="props.row.email_verified_at"
  155. class="py-1 px-2 bg-green-200 text-green-900 rounded-full text-xs"
  156. >
  157. verified
  158. </span>
  159. <button
  160. v-else
  161. @click="resendVerification(props.row.id)"
  162. class="focus:outline-none text-sm"
  163. :class="resendVerificationLoading ? 'cursor-not-allowed' : ''"
  164. :disabled="resendVerificationLoading"
  165. >
  166. Resend email
  167. </button>
  168. </span>
  169. <span v-else class="flex items-center justify-center outline-none" tabindex="-1">
  170. <icon
  171. v-if="!isDefault(props.row.id)"
  172. name="trash"
  173. class="block w-6 h-6 text-grey-300 fill-current cursor-pointer"
  174. @click.native="openDeleteModal(props.row)"
  175. />
  176. </span>
  177. </template>
  178. </vue-good-table>
  179. <Modal :open="addRecipientModalOpen" @close="addRecipientModalOpen = false">
  180. <div class="max-w-lg w-full bg-white rounded-lg shadow-2xl p-6">
  181. <h2
  182. class="font-semibold text-grey-900 text-2xl leading-tight border-b-2 border-grey-100 pb-4"
  183. >
  184. Add new recipient
  185. </h2>
  186. <p class="mt-4 text-grey-700">
  187. Enter the individual email of the new recipient you'd like to add.
  188. </p>
  189. <p class="mt-4 text-grey-700">
  190. You will receive an email with a verification link that will expire in one hour, you can
  191. click "Resend email" to get a new one.
  192. </p>
  193. <div class="mt-6">
  194. <p v-show="errors.newRecipient" class="mb-3 text-red-500 text-sm">
  195. {{ errors.newRecipient }}
  196. </p>
  197. <input
  198. v-model="newRecipient"
  199. type="email"
  200. class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3 mb-6"
  201. :class="errors.newRecipient ? 'border-red-500' : ''"
  202. placeholder="johndoe@example.com"
  203. autofocus
  204. />
  205. <button
  206. @click="validateNewRecipient"
  207. class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:outline-none"
  208. :class="addRecipientLoading ? 'cursor-not-allowed' : ''"
  209. :disabled="addRecipientLoading"
  210. >
  211. Add Recipient
  212. <loader v-if="addRecipientLoading" />
  213. </button>
  214. <button
  215. @click="addRecipientModalOpen = false"
  216. class="ml-4 px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus:outline-none"
  217. >
  218. Cancel
  219. </button>
  220. </div>
  221. </div>
  222. </Modal>
  223. <Modal :open="addRecipientKeyModalOpen" @close="closeRecipientKeyModal">
  224. <div class="max-w-lg w-full bg-white rounded-lg shadow-2xl p-6">
  225. <h2
  226. class="font-semibold text-grey-900 text-2xl leading-tight border-b-2 border-grey-100 pb-4"
  227. >
  228. Add Public GPG Key
  229. </h2>
  230. <p class="mt-4 text-grey-700">Enter your <b>PUBLIC</b> key data in the text area below.</p>
  231. <p class="mt-4 text-grey-700">Make sure to remove <b>Comment:</b> and <b>Version:</b></p>
  232. <div class="mt-6">
  233. <p v-show="errors.recipientKey" class="mb-3 text-red-500 text-sm">
  234. {{ errors.recipientKey }}
  235. </p>
  236. <textarea
  237. v-model="recipientKey"
  238. class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3 mb-6"
  239. :class="errors.recipientKey ? 'border-red-500' : ''"
  240. placeholder="Begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'"
  241. rows="10"
  242. autofocus
  243. >
  244. </textarea>
  245. <button
  246. type="button"
  247. @click="validateRecipientKey"
  248. class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:outline-none"
  249. :class="addRecipientKeyLoading ? 'cursor-not-allowed' : ''"
  250. :disabled="addRecipientKeyLoading"
  251. >
  252. Add Key
  253. <loader v-if="addRecipientKeyLoading" />
  254. </button>
  255. <button
  256. @click="closeRecipientKeyModal"
  257. class="ml-4 px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus:outline-none"
  258. >
  259. Cancel
  260. </button>
  261. </div>
  262. </div>
  263. </Modal>
  264. <Modal :open="deleteRecipientKeyModalOpen" @close="closeDeleteRecipientKeyModal">
  265. <div class="max-w-lg w-full bg-white rounded-lg shadow-2xl p-6">
  266. <h2
  267. class="font-semibold text-grey-900 text-2xl leading-tight border-b-2 border-grey-100 pb-4"
  268. >
  269. Remove recipient public key
  270. </h2>
  271. <p class="mt-4 text-grey-700">
  272. Are you sure you want to remove the public key for this recipient? It will also be removed
  273. from any other recipients using the same key.
  274. </p>
  275. <div class="mt-6">
  276. <button
  277. type="button"
  278. @click="deleteRecipientKey(recipientKeyToDelete)"
  279. class="px-4 py-3 text-white font-semibold bg-red-500 hover:bg-red-600 border border-transparent rounded focus:outline-none"
  280. :class="deleteRecipientKeyLoading ? 'cursor-not-allowed' : ''"
  281. :disabled="deleteRecipientKeyLoading"
  282. >
  283. Remove public key
  284. <loader v-if="deleteRecipientLoading" />
  285. </button>
  286. <button
  287. @click="closeDeleteRecipientKeyModal"
  288. class="ml-4 px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus:outline-none"
  289. >
  290. Cancel
  291. </button>
  292. </div>
  293. </div>
  294. </Modal>
  295. <Modal :open="deleteRecipientModalOpen" @close="closeDeleteModal">
  296. <div class="max-w-lg w-full bg-white rounded-lg shadow-2xl p-6">
  297. <h2
  298. class="font-semibold text-grey-900 text-2xl leading-tight border-b-2 border-grey-100 pb-4"
  299. >
  300. Delete recipient
  301. </h2>
  302. <p class="mt-4 text-grey-700">Are you sure you want to delete this recipient?</p>
  303. <div class="mt-6">
  304. <button
  305. type="button"
  306. @click="deleteRecipient(recipientToDelete)"
  307. class="px-4 py-3 text-white font-semibold bg-red-500 hover:bg-red-600 border border-transparent rounded focus:outline-none"
  308. :class="deleteRecipientLoading ? 'cursor-not-allowed' : ''"
  309. :disabled="deleteRecipientLoading"
  310. >
  311. Delete recipient
  312. <loader v-if="deleteRecipientLoading" />
  313. </button>
  314. <button
  315. @click="closeDeleteModal"
  316. class="ml-4 px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus:outline-none"
  317. >
  318. Cancel
  319. </button>
  320. </div>
  321. </div>
  322. </Modal>
  323. </div>
  324. </template>
  325. <script>
  326. import Modal from './../components/Modal.vue'
  327. import Toggle from './../components/Toggle.vue'
  328. import { roundArrow } from 'tippy.js'
  329. import 'tippy.js/dist/svg-arrow.css'
  330. import 'tippy.js/dist/tippy.css'
  331. import tippy from 'tippy.js'
  332. export default {
  333. props: {
  334. user: {
  335. type: Object,
  336. required: true,
  337. },
  338. initialRecipients: {
  339. type: Array,
  340. required: true,
  341. },
  342. aliasesUsingDefault: {
  343. type: Array,
  344. required: true,
  345. },
  346. aliasesUsingDefaultCount: {
  347. type: Number,
  348. required: true,
  349. },
  350. domain: {
  351. type: String,
  352. required: true,
  353. },
  354. },
  355. components: {
  356. Modal,
  357. Toggle,
  358. },
  359. created() {
  360. this.defaultRecipient = _.find(this.rows, ['id', this.user.default_recipient_id])
  361. this.defaultRecipient.aliases = this.defaultRecipient.aliases.concat(this.aliasesUsingDefault)
  362. },
  363. data() {
  364. return {
  365. defaultRecipient: {},
  366. newRecipient: '',
  367. recipientKey: '',
  368. search: '',
  369. addRecipientLoading: false,
  370. addRecipientModalOpen: false,
  371. recipientToDelete: null,
  372. recipientKeyToDelete: null,
  373. deleteRecipientLoading: false,
  374. deleteRecipientModalOpen: false,
  375. deleteRecipientKeyLoading: false,
  376. deleteRecipientKeyModalOpen: false,
  377. addRecipientKeyLoading: false,
  378. addRecipientKeyModalOpen: false,
  379. recipientToAddKey: {},
  380. resendVerificationLoading: false,
  381. errors: {},
  382. columns: [
  383. {
  384. label: 'Created',
  385. field: 'created_at',
  386. globalSearchDisabled: true,
  387. },
  388. {
  389. label: 'Key',
  390. field: 'key',
  391. type: 'number',
  392. },
  393. {
  394. label: 'Email',
  395. field: 'email',
  396. },
  397. {
  398. label: 'Recipient Aliases',
  399. field: 'aliases',
  400. sortable: true,
  401. sortFn: this.sortRecipientAliases,
  402. globalSearchDisabled: true,
  403. },
  404. {
  405. label: 'Can Reply/Send',
  406. field: 'can_reply_send',
  407. type: 'boolean',
  408. globalSearchDisabled: true,
  409. },
  410. {
  411. label: 'Encryption',
  412. field: 'should_encrypt',
  413. type: 'boolean',
  414. globalSearchDisabled: true,
  415. },
  416. {
  417. label: 'Verified',
  418. field: 'email_verified_at',
  419. globalSearchDisabled: true,
  420. },
  421. {
  422. label: '',
  423. field: 'actions',
  424. sortable: false,
  425. globalSearchDisabled: true,
  426. },
  427. ],
  428. rows: this.initialRecipients,
  429. tippyInstance: null,
  430. }
  431. },
  432. watch: {
  433. addRecipientKeyModalOpen: _.debounce(function () {
  434. this.addTooltips()
  435. }, 50),
  436. },
  437. methods: {
  438. addTooltips() {
  439. if (this.tippyInstance) {
  440. _.each(this.tippyInstance, instance => instance.destroy())
  441. }
  442. this.tippyInstance = tippy('.tooltip', {
  443. arrow: roundArrow,
  444. allowHTML: true,
  445. })
  446. },
  447. debounceToolips: _.debounce(function () {
  448. this.addTooltips()
  449. }, 50),
  450. aliasesTooltip(aliases, isDefault) {
  451. let ellipses =
  452. aliases.length > 5 || (isDefault && this.aliasesUsingDefaultCount > 5) ? '...' : ''
  453. return (
  454. _.reduce(_.take(aliases, 5), (list, alias) => list + `${alias.email}<br>`, '') + ellipses
  455. )
  456. },
  457. isDefault(id) {
  458. return this.user.default_recipient_id === id
  459. },
  460. validateNewRecipient(e) {
  461. this.errors = {}
  462. if (!this.newRecipient) {
  463. this.errors.newRecipient = 'Email required'
  464. } else if (!this.validEmail(this.newRecipient)) {
  465. this.errors.newRecipient = 'Valid Email required'
  466. }
  467. if (!this.errors.newRecipient) {
  468. this.addNewRecipient()
  469. }
  470. e.preventDefault()
  471. },
  472. addNewRecipient() {
  473. this.addRecipientLoading = true
  474. axios
  475. .post(
  476. '/api/v1/recipients',
  477. JSON.stringify({
  478. email: this.newRecipient,
  479. }),
  480. {
  481. headers: { 'Content-Type': 'application/json' },
  482. }
  483. )
  484. .then(({ data }) => {
  485. this.addRecipientLoading = false
  486. data.data.key = this.rows.length + 1
  487. this.rows.push(data.data)
  488. this.newRecipient = ''
  489. this.addRecipientModalOpen = false
  490. this.success('Recipient created and verification email sent')
  491. })
  492. .catch(error => {
  493. this.addRecipientLoading = false
  494. if (error.response.status === 422) {
  495. this.error(error.response.data.errors.email[0])
  496. } else if (error.response.status === 429) {
  497. this.error('You are making too many requests')
  498. } else {
  499. this.error()
  500. }
  501. })
  502. },
  503. resendVerification(id) {
  504. this.resendVerificationLoading = true
  505. axios
  506. .post(
  507. '/recipients/email/resend',
  508. JSON.stringify({
  509. recipient_id: id,
  510. }),
  511. {
  512. headers: { 'Content-Type': 'application/json' },
  513. }
  514. )
  515. .then(({ data }) => {
  516. this.resendVerificationLoading = false
  517. this.success('Verification email resent')
  518. })
  519. .catch(error => {
  520. this.resendVerificationLoading = false
  521. if (error.response.status === 429) {
  522. this.error('You can only resend the email once per minute')
  523. } else {
  524. this.error()
  525. }
  526. })
  527. },
  528. openDeleteModal(recipient) {
  529. this.deleteRecipientModalOpen = true
  530. this.recipientToDelete = recipient
  531. },
  532. closeDeleteModal() {
  533. this.deleteRecipientModalOpen = false
  534. this.recipientToDelete = null
  535. },
  536. deleteRecipient(recipient) {
  537. this.deleteRecipientLoading = true
  538. axios
  539. .delete(`/api/v1/recipients/${recipient.id}`)
  540. .then(response => {
  541. recipient.should_encrypt = false
  542. recipient.fingerprint = null
  543. this.rows = _.reject(this.rows, row => row.id === recipient.id)
  544. this.deleteRecipientModalOpen = false
  545. this.deleteRecipientLoading = false
  546. })
  547. .catch(error => {
  548. this.error()
  549. this.deleteRecipientLoading = false
  550. this.deleteRecipientModalOpen = false
  551. })
  552. },
  553. openDeleteRecipientKeyModal(recipient) {
  554. this.deleteRecipientKeyModalOpen = true
  555. this.recipientKeyToDelete = recipient
  556. },
  557. closeDeleteRecipientKeyModal() {
  558. this.deleteRecipientKeyModalOpen = false
  559. this.recipientKeyIdToDelete = null
  560. },
  561. deleteRecipientKey(recipient) {
  562. this.deleteRecipientKeyLoading = true
  563. axios
  564. .delete(`/api/v1/recipient-keys/${recipient.id}`)
  565. .then(response => {
  566. recipient.should_encrypt = false
  567. recipient.fingerprint = null
  568. this.deleteRecipientKeyModalOpen = false
  569. this.deleteRecipientKeyLoading = false
  570. })
  571. .catch(error => {
  572. if (error.response !== undefined) {
  573. this.error(error.response.data)
  574. } else {
  575. this.error()
  576. }
  577. this.deleteRecipientKeyLoading = false
  578. this.deleteRecipientKeyModalOpen = false
  579. })
  580. },
  581. validateRecipientKey(e) {
  582. this.errors = {}
  583. if (!this.recipientKey) {
  584. this.errors.recipientKey = 'Key required'
  585. } else if (!this.validKey(this.recipientKey)) {
  586. this.errors.recipientKey = 'Valid Key required'
  587. }
  588. if (!this.errors.recipientKey) {
  589. this.addRecipientKey()
  590. }
  591. e.preventDefault()
  592. },
  593. addRecipientKey() {
  594. this.addRecipientKeyLoading = true
  595. axios
  596. .patch(
  597. `/api/v1/recipient-keys/${this.recipientToAddKey.id}`,
  598. JSON.stringify({
  599. key_data: this.recipientKey,
  600. }),
  601. {
  602. headers: { 'Content-Type': 'application/json' },
  603. }
  604. )
  605. .then(({ data }) => {
  606. this.addRecipientKeyLoading = false
  607. let recipient = _.find(this.rows, ['id', this.recipientToAddKey.id])
  608. recipient.should_encrypt = data.data.should_encrypt
  609. recipient.fingerprint = data.data.fingerprint
  610. this.recipientKey = ''
  611. this.addRecipientKeyModalOpen = false
  612. this.success(
  613. `Key Successfully Added for ${this.recipientToAddKey.email}. Make sure to check the fingerprint is correct!`
  614. )
  615. })
  616. .catch(error => {
  617. this.addRecipientKeyLoading = false
  618. if (error.response !== undefined) {
  619. this.error(error.response.data)
  620. } else {
  621. this.error()
  622. }
  623. })
  624. },
  625. turnOnEncryption(id) {
  626. axios
  627. .post(
  628. `/api/v1/encrypted-recipients`,
  629. JSON.stringify({
  630. id: id,
  631. }),
  632. {
  633. headers: { 'Content-Type': 'application/json' },
  634. }
  635. )
  636. .then(response => {
  637. //
  638. })
  639. .catch(error => {
  640. this.error()
  641. })
  642. },
  643. turnOffEncryption(id) {
  644. axios
  645. .delete(`/api/v1/encrypted-recipients/${id}`)
  646. .then(response => {
  647. //
  648. })
  649. .catch(error => {
  650. this.error()
  651. })
  652. },
  653. allowRepliesSends(id) {
  654. axios
  655. .post(
  656. `/api/v1/allowed-recipients`,
  657. JSON.stringify({
  658. id: id,
  659. }),
  660. {
  661. headers: { 'Content-Type': 'application/json' },
  662. }
  663. )
  664. .then(response => {
  665. //
  666. })
  667. .catch(error => {
  668. this.error()
  669. })
  670. },
  671. disallowRepliesSends(id) {
  672. axios
  673. .delete(`/api/v1/allowed-recipients/${id}`)
  674. .then(response => {
  675. //
  676. })
  677. .catch(error => {
  678. this.error()
  679. })
  680. },
  681. openRecipientKeyModal(recipient) {
  682. this.addRecipientKeyModalOpen = true
  683. this.recipientToAddKey = recipient
  684. },
  685. closeRecipientKeyModal() {
  686. this.addRecipientKeyModalOpen = false
  687. this.recipientToAddKey = {}
  688. },
  689. validEmail(email) {
  690. let re =
  691. /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
  692. return re.test(email)
  693. },
  694. validKey(key) {
  695. let re =
  696. /-----BEGIN PGP PUBLIC KEY BLOCK-----([A-Za-z0-9+=\/\n]+)-----END PGP PUBLIC KEY BLOCK-----/i
  697. return re.test(key)
  698. },
  699. clipboardSuccess() {
  700. this.success('Copied to clipboard')
  701. },
  702. clipboardError() {
  703. this.error('Could not copy to clipboard')
  704. },
  705. success(text = '') {
  706. this.$notify({
  707. title: 'Success',
  708. text: text,
  709. type: 'success',
  710. })
  711. },
  712. error(text = 'An error has occurred, please try again later') {
  713. this.$notify({
  714. title: 'Error',
  715. text: text,
  716. type: 'error',
  717. })
  718. },
  719. },
  720. }
  721. </script>