Added send from alias address creator

This commit is contained in:
Will Browning
2020-12-20 12:24:08 +00:00
parent 47f7ada2a6
commit f782a399dc
31 changed files with 2218 additions and 1944 deletions

View File

@@ -2,6 +2,6 @@
"printWidth": 100,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"semi": false
"semi": false,
"arrowParens": "avoid"
}

596
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,9 +5,9 @@ current:
major: 0
minor: 6
patch: 1
prerelease: ''
prerelease: 1-g47f7ada
buildmetadata: ''
commit: f1d596
commit: 47f7ad
timestamp:
year: 2020
month: 10

2748
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,17 +11,17 @@
"format": "prettier --write 'resources/**/*.{css,js,vue}'"
},
"dependencies": {
"axios": "^0.19",
"cross-env": "^7.0",
"dayjs": "^1.9.6",
"autoprefixer": "^9.8.6",
"axios": "^0.21.0",
"cross-env": "^7.0.3",
"dayjs": "^1.9.7",
"laravel-mix": "^5.0.9",
"lodash": "^4.17.20",
"portal-vue": "^2.1.7",
"postcss-import": "^11.1.0",
"postcss-nesting": "^5.0.0",
"postcss": "^7.0.35",
"resolve-url-loader": "^3.1.2",
"tailwindcss": "^1.9.5",
"tippy.js": "^4.3.5",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.2",
"tippy.js": "^6.2.7",
"v-clipboard": "^2.2.3",
"vue": "^2.6.12",
"vue-good-table": "^2.21.1",
@@ -31,9 +31,9 @@
"vuedraggable": "^2.24.2"
},
"devDependencies": {
"husky": "^2.7.0",
"lint-staged": "^8.2.1",
"prettier": "1.16.4"
"husky": "^4.3.6",
"lint-staged": "^10.5.3",
"prettier": "2.2.1"
},
"husky": {
"hooks": {
@@ -42,12 +42,10 @@
},
"lint-staged": {
"*.{css,js,vue}": [
"npm run format --",
"git add"
"npm run format --"
],
"*.php": [
"composer format",
"git add"
"composer format"
]
}
}

View File

@@ -256,7 +256,7 @@ table.vgt-table tr.clickable:hover {
}
.vgt-wrap__footer .footer__navigation__page-btn {
@apply no-underline text-grey-500 font-bold whitespace-no-wrap;
@apply no-underline text-grey-500 font-bold whitespace-nowrap;
}
.vgt-wrap__footer .footer__navigation__page-btn:focus {

View File

@@ -264,6 +264,21 @@
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
<svg
v-else-if="name === 'more'"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="1"></circle>
<circle cx="12" cy="5" r="1"></circle>
<circle cx="12" cy="19" r="1"></circle>
</svg>
</template>
<script>

View File

@@ -8,7 +8,7 @@
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
style="enable-background: new 0 0 50 50"
xml:space="preserve"
>
<path

View File

@@ -79,7 +79,7 @@ export default {
},
watch: {
open: {
handler: function(newValue) {
handler: function (newValue) {
if (newValue) {
this.show()
} else {

View File

@@ -0,0 +1,65 @@
<template>
<div class="relative flex justify-end items-center" @keydown.escape="isOpen = false">
<button
ref="openOptions"
@click="isOpen = !isOpen"
:aria-expanded="isOpen"
id="project-options-menu-0"
aria-has-popup="true"
type="button"
class="w-8 h-8 bg-white inline-flex items-center justify-center text-grey-400 rounded-full hover:text-grey-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
>
<span class="sr-only">Open options</span>
<icon
name="more"
class="block w-6 h-6 text-grey-300 fill-current cursor-pointer outline-none"
aria-hidden="true"
/>
</button>
<transition
enter-active-class="transition ease-out duration-100"
enter-class="transform opacity-0 scale-95"
enter-to-class="transform opacity-100 scale-100"
leave-active-class="transition ease-in duration-75"
leave-class="transform opacity-100 scale-100"
leave-to-class="transform opacity-0 scale-95"
>
<div
v-show="isOpen"
class="mx-3 origin-top-right absolute right-7 top-0 w-48 mt-1 rounded-md shadow-lg z-10 bg-white ring-1 ring-black ring-opacity-5 divide-y divide-grey-200"
role="menu"
aria-orientation="vertical"
aria-labelledby="project-options-menu-0"
>
<slot></slot>
</div>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
isOpen: false,
}
},
created() {
window.addEventListener('click', this.close)
},
beforeDestroy() {
window.removeEventListener('click', this.close)
},
methods: {
close(e) {
if (!this.$refs.openOptions.contains(e.target)) {
this.isOpen = false
}
},
},
}
</script>

View File

@@ -1,21 +1,44 @@
<template>
<span
class="relative outline-none cursor-pointer h-6 w-12 rounded-full"
<button
@click="toggle"
role="checkbox"
:aria-checked="value.toString()"
tabindex="0"
@keydown.space.prevent="toggle"
type="button"
:aria-pressed="value.toString()"
:class="this.value ? 'bg-cyan-500' : 'bg-grey-300'"
class="relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none"
>
<span class="sr-only">Use setting</span>
<span
class="toggle-background inline-block rounded-full h-full w-full shadow-inner"
:class="this.value ? 'bg-cyan-500' : 'bg-grey-300'"
></span>
<span
class="toggle-indicator absolute bg-white rounded-full shadow w-4 h-4"
:style="indicatorStyles"
></span>
</span>
:class="this.value ? 'translate-x-5' : 'translate-x-0'"
class="relative inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
>
<span
:class="this.value ? 'opacity-0 ease-out duration-100' : 'opacity-100 ease-in duration-200'"
class="absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"
aria-hidden="true"
>
<svg class="h-3 w-3 text-grey-400" fill="none" viewBox="0 0 12 12">
<path
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
<span
:class="this.value ? 'opacity-100 ease-in duration-200' : 'opacity-0 ease-out duration-100'"
class="absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"
aria-hidden="true"
>
<svg class="h-3 w-3 text-cyan-500" fill="currentColor" viewBox="0 0 12 12">
<path
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
/>
</svg>
</span>
</span>
</button>
</template>
<script>
@@ -27,22 +50,5 @@ export default {
this.value ? this.$emit('off') : this.$emit('on')
},
},
computed: {
indicatorStyles() {
return { transform: this.value ? 'translateX(1.5rem)' : 'translateX(0)' }
},
},
}
</script>
<style>
.toggle-background {
transition: background-color 0.2s ease;
}
.toggle-indicator {
top: 0.25rem;
left: 0.25rem;
transition: transform 0.2s ease;
}
</style>

View File

@@ -1,9 +1,7 @@
<template>
<div>
<div class="mt-6">
<h3 class="font-bold text-xl">
Device Authentication (U2F)
</h3>
<h3 class="font-bold text-xl">Device Authentication (U2F)</h3>
<div class="my-4 w-24 border-b-2 border-grey-200"></div>
@@ -13,9 +11,7 @@
</p>
<div>
<p class="mb-0" v-if="keys.length === 0">
You have not registered any Webauthn Keys.
</p>
<p class="mb-0" v-if="keys.length === 0">You have not registered any Webauthn Keys.</p>
<div class="table w-full text-sm md:text-base" v-if="keys.length > 0">
<div class="table-row">

View File

@@ -1,8 +1,6 @@
<template>
<div>
<h3 class="font-bold text-xl">
Information
</h3>
<h3 class="font-bold text-xl">Information</h3>
<div class="mt-4 w-24 border-b-2 border-grey-200"></div>
@@ -44,9 +42,7 @@
</button>
<div class="mt-6">
<h3 class="font-bold text-xl">
Personal Access Tokens
</h3>
<h3 class="font-bold text-xl">Personal Access Tokens</h3>
<div class="my-4 w-24 border-b-2 border-grey-200"></div>
@@ -106,9 +102,7 @@
</li>
</ul>
</div>
<label for="create-token-name" class="block text-grey-700 text-sm my-2">
Name:
</label>
<label for="create-token-name" class="block text-grey-700 text-sm my-2"> Name: </label>
<input
v-model="form.name"
type="text"

View File

@@ -11,9 +11,7 @@
/>
<div class="font-bold text-xl md:text-3xl text-indigo-800">
{{ totalActive }}
<p class="text-grey-300 text-sm tracking-wide uppercase">
Active
</p>
<p class="text-grey-300 text-sm tracking-wide uppercase">Active</p>
</div>
</div>
</div>
@@ -27,9 +25,7 @@
/>
<div class="font-bold text-xl md:text-3xl text-indigo-800">
{{ totalInactive }}
<p class="text-grey-300 text-sm tracking-wide uppercase">
Inactive
</p>
<p class="text-grey-300 text-sm tracking-wide uppercase">Inactive</p>
</div>
</div>
</div>
@@ -43,9 +39,7 @@
/>
<div class="font-bold text-xl md:text-3xl text-indigo-800">
{{ totalForwarded }}
<p class="text-grey-300 text-sm tracking-wide uppercase">
Emails Forwarded
</p>
<p class="text-grey-300 text-sm tracking-wide uppercase">Emails Forwarded</p>
</div>
</div>
</div>
@@ -59,9 +53,7 @@
/>
<div class="font-bold text-xl md:text-3xl text-indigo-800">
{{ totalBlocked }}
<p class="text-grey-300 text-sm tracking-wide uppercase">
Emails Blocked
</p>
<p class="text-grey-300 text-sm tracking-wide uppercase">Emails Blocked</p>
</div>
</div>
</div>
@@ -75,9 +67,7 @@
/>
<div class="font-bold text-xl md:text-3xl text-indigo-800">
{{ totalReplies }}
<p class="text-grey-300 text-sm tracking-wide uppercase">
Email Replies
</p>
<p class="text-grey-300 text-sm tracking-wide uppercase">Email Replies</p>
</div>
</div>
</div>
@@ -122,7 +112,7 @@
<div class="block relative mr-4">
<select
v-model="showAliases"
class="block appearance-none w-full text-grey-700 bg-white p-3 pr-8 rounded shadow focus:shadow-outline"
class="block appearance-none w-full text-grey-700 bg-white p-3 pr-8 rounded shadow focus:ring"
required
>
<option value="without">Hide Deleted</option>
@@ -148,7 +138,7 @@
@click="generateAliasModalOpen = true"
class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:outline-none ml-auto"
>
Generate New Alias
Create New Alias
</button>
</div>
</div>
@@ -182,11 +172,23 @@
No aliases found for that search!
</div>
<template slot="table-row" slot-scope="props">
<span
v-if="props.column.field == 'created_at'"
class="tooltip outline-none text-sm"
:data-tippy-content="rows[props.row.originalIndex].created_at | formatDate"
>{{ props.row.created_at | timeAgo }}
<span v-if="props.column.field == 'created_at'" class="flex items-center">
<span
:class="`bg-${getAliasStatus(props.row).colour}-100`"
class="tooltip outline-none h-4 w-4 rounded-full flex items-center justify-center mr-2"
:data-tippy-content="getAliasStatus(props.row).status"
tabindex="-1"
>
<span
:class="`bg-${getAliasStatus(props.row).colour}-400`"
class="h-2 w-2 rounded-full"
></span>
</span>
<span
class="tooltip outline-none text-sm whitespace-nowrap"
:data-tippy-content="rows[props.row.originalIndex].created_at | formatDate"
>{{ props.row.created_at | timeAgo }}
</span>
</span>
<span v-else-if="props.column.field == 'email'" class="block">
<span
@@ -306,18 +308,44 @@
/>
</span>
<span v-else class="flex items-center justify-center outline-none" tabindex="-1">
<icon
v-if="props.row.deleted_at"
name="undo"
class="block w-6 h-6 text-grey-300 fill-current cursor-pointer outline-none"
@click.native="openRestoreModal(props.row.id)"
/>
<icon
v-else
name="trash"
class="block w-6 h-6 text-grey-300 fill-current cursor-pointer outline-none"
@click.native="openDeleteModal(props.row.id)"
/>
<more-options>
<div role="none">
<span
@click="openSendFromModal(props.row)"
class="group cursor-pointer flex items-center px-4 py-3 text-sm text-grey-700 hover:bg-grey-100 hover:text-grey-900"
role="menuitem"
>
<icon name="send" class="block mr-3 w-5 h-5 text-grey-300 outline-none" />
Send From
</span>
</div>
<div v-if="props.row.deleted_at" role="none">
<span
@click="openRestoreModal(props.row.id)"
class="group cursor-pointer flex items-center px-4 py-3 text-sm text-grey-700 hover:bg-grey-100 hover:text-grey-900"
role="menuitem"
>
<icon
name="undo"
class="block mr-3 w-5 h-5 text-grey-300 fill-current outline-none"
/>
Restore
</span>
</div>
<div v-else role="none">
<span
@click="openDeleteModal(props.row.id)"
class="group cursor-pointer flex items-center px-4 py-3 text-sm text-grey-700 hover:bg-grey-100 hover:text-grey-900"
role="menuitem"
>
<icon
name="trash"
class="block mr-3 w-5 h-5 text-grey-300 fill-current outline-none"
/>
Delete
</span>
</div>
</more-options>
</span>
</template>
</vue-good-table>
@@ -328,9 +356,7 @@
It doesn't look like you have any aliases yet!
</h1>
<div class="mx-auto mb-6 w-24 border-b-2 border-grey-200"></div>
<p class="mb-4">
There are two ways to create new aliases.
</p>
<p class="mb-4">There are two ways to create new aliases.</p>
<h3 class="mb-4 text-xl text-indigo-800 font-semibold">
Option 1: Create aliases on the fly
</h3>
@@ -375,28 +401,23 @@
<h2
class="font-semibold text-grey-900 text-2xl leading-tight border-b-2 border-grey-100 pb-4"
>
Generate new alias
Create new alias
</h2>
<p class="mt-4 text-grey-700">
Other aliases e.g. alias@{{ subdomain }} can also be created automatically when they
receive their first email.
</p>
<label for="alias_domain" class="block text-grey-700 text-sm my-2">
Alias Domain:
</label>
<label for="alias_domain" class="block text-grey-700 text-sm my-2"> Alias Domain: </label>
<div class="block relative w-full mb-4">
<select
v-model="generateAliasDomain"
id="alias_domain"
class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline"
class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring"
required
>
<option
v-for="domainOption in domainOptions"
:key="domainOption"
:value="domainOption"
>{{ domainOption }}</option
>
<option v-for="domainOption in domainOptions" :key="domainOption" :value="domainOption">
{{ domainOption }}
</option>
</select>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
@@ -420,15 +441,16 @@
<select
v-model="generateAliasFormat"
id="alias_domain"
class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline"
class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring"
required
>
<option
v-for="formatOption in aliasFormatOptions"
:key="formatOption.value"
:value="formatOption.value"
>{{ formatOption.label }}</option
>
{{ formatOption.label }}
</option>
</select>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
@@ -486,7 +508,7 @@
:class="generateAliasLoading ? 'cursor-not-allowed' : ''"
:disabled="generateAliasLoading"
>
Generate Alias
Create Alias
<loader v-if="generateAliasLoading" />
</button>
<button
@@ -609,12 +631,114 @@
</div>
</div>
</Modal>
<Modal :open="sendFromAliasModalOpen" @close="closeSendFromModal">
<div class="max-w-lg w-full bg-white rounded-lg shadow-2xl px-6 py-6">
<h2
class="font-semibold text-grey-900 text-2xl leading-tight border-b-2 border-grey-100 pb-4"
>
Send from alias
</h2>
<p class="mt-4 text-grey-700">
Use this to automatically create the correct address to send an email to in order to send
an <b>email from this alias</b>.
</p>
<label for="send_from_alias" class="block text-grey-700 text-sm my-2"> Alias: </label>
<input
v-model="aliasToSendFrom.email"
id="send_from_alias"
type="text"
class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3"
disabled
/>
<label for="send_from_alias_destination" class="block text-grey-700 text-sm my-2">
Email destination:
</label>
<p v-show="errors.sendFromAliasDestination" class="mb-3 text-red-500 text-sm">
{{ errors.sendFromAliasDestination }}
</p>
<input
v-model="sendFromAliasDestination"
id="send_from_alias_destination"
type="text"
class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3"
:class="errors.sendFromAliasDestination ? 'border-red-500' : ''"
placeholder="Enter email..."
autofocus
/>
<div v-if="sendFromAliasEmailToSendTo">
<p for="alias_domain" class="block text-grey-700 text-sm my-2">
Send your message to this email:
</p>
<div
v-clipboard="() => sendFromAliasEmailToSendTo"
v-clipboard:success="setSendFromAliasCopied"
class="flex items-center justify-between cursor-pointer text-xs border-t-4 rounded-sm text-green-800 border-green-600 bg-green-100 p-2 mb-3"
role="alert"
>
<span>
{{ sendFromAliasEmailToSendTo }}
</span>
<svg
v-if="sendFromAliasCopied"
viewBox="0 0 24 24"
width="20"
height="20"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 11 12 14 22 4"></polyline>
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
</svg>
<svg
v-else
viewBox="0 0 24 24"
width="20"
height="20"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</div>
</div>
<div class="mt-6">
<button
type="button"
@click="displaySendFromAddress(aliasToSendFrom)"
class="px-4 py-3 text-cyan-900 font-semibold bg-cyan-400 hover:bg-cyan-300 border border-transparent rounded focus:outline-none"
:class="sendFromAliasLoading ? 'cursor-not-allowed' : ''"
:disabled="sendFromAliasLoading"
>
Show address
<loader v-if="sendFromAliasLoading" />
</button>
<button
@click="closeSendFromModal"
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"
>
Cancel
</button>
</div>
</div>
</Modal>
</div>
</template>
<script>
import Modal from './../components/Modal.vue'
import Toggle from './../components/Toggle.vue'
import MoreOptions from './../components/MoreOptions.vue'
import { roundArrow } from 'tippy.js'
import 'tippy.js/dist/svg-arrow.css'
import 'tippy.js/dist/tippy.css'
import tippy from 'tippy.js'
import Multiselect from 'vue-multiselect'
@@ -677,9 +801,7 @@ export default {
Modal,
Toggle,
Multiselect,
},
mounted() {
this.addTooltips()
MoreOptions,
},
data() {
return {
@@ -688,9 +810,15 @@ export default {
aliasIdToEdit: '',
aliasDescriptionToEdit: '',
aliasIdToDelete: '',
aliasToSendFrom: {},
sendFromAliasDestination: '',
sendFromAliasEmailToSendTo: '',
sendFromAliasCopied: false,
aliasIdToRestore: '',
deleteAliasLoading: false,
deleteAliasModalOpen: false,
sendFromAliasLoading: false,
sendFromAliasModalOpen: false,
restoreAliasLoading: false,
restoreAliasModalOpen: false,
editAliasRecipientsLoading: false,
@@ -776,16 +904,11 @@ export default {
},
],
rows: this.initialAliases,
tippyInstance: null,
errors: {},
}
},
watch: {
aliasIdToEdit: _.debounce(function() {
this.addTooltips()
}, 50),
editAliasRecipientsModalOpen: _.debounce(function() {
this.addTooltips()
}, 50),
showAliases(value) {
this.updateAliases()
},
@@ -803,12 +926,16 @@ export default {
},
methods: {
addTooltips() {
tippy('.tooltip', {
arrow: true,
arrowType: 'round',
if (this.tippyInstance) {
_.each(this.tippyInstance, instance => instance.destroy())
}
this.tippyInstance = tippy('.tooltip', {
arrow: roundArrow,
allowHTML: true,
})
},
debounceToolips: _.debounce(function() {
debounceToolips: _.debounce(function () {
this.addTooltips()
}, 50),
recipientsTooltip(recipients) {
@@ -822,6 +949,17 @@ export default {
this.deleteAliasModalOpen = false
this.aliasIdToDelete = ''
},
openSendFromModal(alias) {
this.sendFromAliasDestination = ''
this.sendFromAliasEmailToSendTo = ''
this.sendFromAliasCopied = false
this.sendFromAliasModalOpen = true
this.aliasToSendFrom = alias
},
closeSendFromModal() {
this.sendFromAliasModalOpen = false
this.aliasToSendFrom = {}
},
openRestoreModal(id) {
this.restoreAliasModalOpen = true
this.aliasIdToRestore = id
@@ -1021,6 +1159,21 @@ export default {
this.error()
})
},
displaySendFromAddress(alias) {
this.errors = {}
if (!this.validEmail(this.sendFromAliasDestination)) {
this.errors.sendFromAliasDestination = 'Valid Email required'
return
}
this.sendFromAliasEmailToSendTo = `${
alias.local_part
}+${this.sendFromAliasDestination.replace('@', '=')}@${alias.domain}`
},
setSendFromAliasCopied() {
this.sendFromAliasCopied = true
},
getAliasEmail(alias) {
return alias.extension
? `${alias.local_part}+${alias.extension}@${alias.domain}`
@@ -1029,6 +1182,19 @@ export default {
getAliasLocalPart(alias) {
return alias.extension ? `${alias.local_part}+${alias.extension}` : alias.local_part
},
getAliasStatus(alias) {
if (alias.deleted_at) {
return {
colour: 'red',
status: 'Deleted',
}
} else {
return {
colour: alias.active ? 'green' : 'grey',
status: alias.active ? 'Active' : 'Inactive',
}
}
},
sortRecipients(x, y) {
return x.length < y.length ? -1 : x.length > y.length ? 1 : 0
},
@@ -1039,6 +1205,10 @@ export default {
let re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))$/
return re.test(part)
},
validEmail(email) {
let re = /^(([^<>()\[\]\\.,;:\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,}))$/
return re.test(email)
},
clipboardSuccess() {
this.success('Copied to clipboard')
},

View File

@@ -186,9 +186,7 @@
To get started all you have to do is add a TXT record to your domain to verify ownership
and then add the domain here by clicking the button above.
</p>
<p class="mb-4">
The TXT record needs to have the following values:
</p>
<p class="mb-4">The TXT record needs to have the following values:</p>
<p class="mb-4">
Type: <b>TXT</b><br />
Host: <b>@</b><br />
@@ -396,6 +394,9 @@
<script>
import Modal from './../components/Modal.vue'
import Toggle from './../components/Toggle.vue'
import { roundArrow } from 'tippy.js'
import 'tippy.js/dist/svg-arrow.css'
import 'tippy.js/dist/tippy.css'
import tippy from 'tippy.js'
import Multiselect from 'vue-multiselect'
@@ -427,9 +428,6 @@ export default {
Toggle,
Multiselect,
},
mounted() {
this.addTooltips()
},
data() {
return {
newDomain: '',
@@ -499,21 +497,26 @@ export default {
},
],
rows: this.initialDomains,
tippyInstance: null,
}
},
watch: {
domainIdToEdit: _.debounce(function() {
domainIdToEdit: _.debounce(function () {
this.addTooltips()
}, 50),
},
methods: {
addTooltips() {
tippy('.tooltip', {
arrow: true,
arrowType: 'round',
if (this.tippyInstance) {
_.each(this.tippyInstance, instance => instance.destroy())
}
this.tippyInstance = tippy('.tooltip', {
arrow: roundArrow,
allowHTML: true,
})
},
debounceToolips: _.debounce(function() {
debounceToolips: _.debounce(function () {
this.addTooltips()
}, 50),
validateNewDomain(e) {

View File

@@ -55,13 +55,7 @@
Key
<span
class="tooltip outline-none"
: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.`
"
: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.`"
>
<icon name="info" class="inline-block w-4 h-4 text-grey-300 fill-current" />
</span>
@@ -328,6 +322,9 @@
<script>
import Modal from './../components/Modal.vue'
import Toggle from './../components/Toggle.vue'
import { roundArrow } from 'tippy.js'
import 'tippy.js/dist/svg-arrow.css'
import 'tippy.js/dist/tippy.css'
import tippy from 'tippy.js'
export default {
@@ -361,9 +358,6 @@ export default {
this.defaultRecipient = _.find(this.rows, ['id', this.user.default_recipient_id])
this.defaultRecipient.aliases = this.defaultRecipient.aliases.concat(this.aliasesUsingDefault)
},
mounted() {
this.addTooltips()
},
data() {
return {
defaultRecipient: {},
@@ -424,21 +418,26 @@ export default {
},
],
rows: this.initialRecipients,
tippyInstance: null,
}
},
watch: {
addRecipientKeyModalOpen: _.debounce(function() {
addRecipientKeyModalOpen: _.debounce(function () {
this.addTooltips()
}, 50),
},
methods: {
addTooltips() {
tippy('.tooltip', {
arrow: true,
arrowType: 'round',
if (this.tippyInstance) {
_.each(this.tippyInstance, instance => instance.destroy())
}
this.tippyInstance = tippy('.tooltip', {
arrow: roundArrow,
allowHTML: true,
})
},
debounceToolips: _.debounce(function() {
debounceToolips: _.debounce(function () {
this.addTooltips()
}, 50),
aliasesTooltip(aliases, isDefault) {
@@ -618,9 +617,7 @@ export default {
this.recipientKey = ''
this.addRecipientKeyModalOpen = false
this.success(
`Key Successfully Added for ${
this.recipientToAddKey.email
}. Make sure to check the fingerprint is correct!`
`Key Successfully Added for ${this.recipientToAddKey.email}. Make sure to check the fingerprint is correct!`
)
})
.catch(error => {

View File

@@ -67,9 +67,7 @@
It doesn't look like you have any rules yet!
</h1>
<div class="mx-auto mb-6 w-24 border-b-2 border-grey-200"></div>
<p class="mb-4">
Click the button above to create a new rule.
</p>
<p class="mb-4">Click the button above to create a new rule.</p>
</div>
</div>
@@ -85,9 +83,7 @@
actions will be added over time.
</p>
<label for="rule_name" class="block text-grey-700 text-sm my-2">
Name:
</label>
<label for="rule_name" class="block text-grey-700 text-sm my-2"> Name: </label>
<p v-show="errors.ruleName" class="mb-3 text-red-500 text-sm">
{{ errors.ruleName }}
</p>
@@ -112,11 +108,11 @@
<select
v-model="createRuleObject.operator"
id="rule_operator"
class="block appearance-none w-full text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none w-full text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option value="AND">AND </option>
<option value="OR">OR </option>
<option value="AND">AND</option>
<option value="OR">OR</option>
</select>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
@@ -143,14 +139,15 @@
<select
v-model="createRuleObject.conditions[key].type"
id="rule_condition_types"
class="block appearance-none w-32 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none w-32 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option
v-for="option in conditionTypeOptions"
:key="option.value"
:value="option.value"
>{{ option.label }}
>
{{ option.label }}
</option>
</select>
<div
@@ -177,14 +174,15 @@
<select
v-model="createRuleObject.conditions[key].match"
id="rule_condition_matches"
class="block appearance-none w-40 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none w-40 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option
v-for="option in conditionMatchOptions(createRuleObject, key)"
:key="option"
:value="option"
>{{ option }}
>
{{ option }}
</option>
</select>
<div
@@ -275,9 +273,7 @@
<div v-for="(action, key) in createRuleObject.actions" :key="key">
<!-- AND/OR operator -->
<div v-if="key !== 0" class="flex justify-center my-2">
<div class="relative">
AND
</div>
<div class="relative">AND</div>
</div>
<div class="p-2 w-full bg-grey-100">
@@ -290,14 +286,15 @@
v-model="createRuleObject.actions[key].type"
@change="ruleActionChange(createRuleObject.actions[key])"
id="rule_action_types"
class="block appearance-none text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option
v-for="option in actionTypeOptions"
:key="option.value"
:value="option.value"
>{{ option.label }}
>
{{ option.label }}
</option>
</select>
<div
@@ -319,7 +316,7 @@
<span
v-if="
createRuleObject.actions[key].type === 'subject' ||
createRuleObject.actions[key].type === 'displayFrom'
createRuleObject.actions[key].type === 'displayFrom'
"
class="ml-4 flex"
>
@@ -343,12 +340,12 @@
<select
v-model="createRuleObject.actions[key].value"
id="create_rule_action_banner"
class="block appearance-none w-40 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none w-40 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option selected value="top">Top </option>
<option selected value="bottom">Bottom </option>
<option selected value="off">Off </option>
<option selected value="top">Top</option>
<option selected value="bottom">Bottom</option>
<option selected value="off">Off</option>
</select>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
@@ -423,9 +420,7 @@
actions will be added over time.
</p>
<label for="edit_rule_name" class="block text-grey-700 text-sm my-2">
Name:
</label>
<label for="edit_rule_name" class="block text-grey-700 text-sm my-2"> Name: </label>
<p v-show="errors.ruleName" class="mb-3 text-red-500 text-sm">
{{ errors.ruleName }}
</p>
@@ -450,11 +445,11 @@
<select
v-model="editRuleObject.operator"
id="edit_rule_operator"
class="block appearance-none w-full text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none w-full text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option value="AND">AND </option>
<option value="OR">OR </option>
<option value="AND">AND</option>
<option value="OR">OR</option>
</select>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
@@ -481,14 +476,15 @@
<select
v-model="editRuleObject.conditions[key].type"
id="edit_rule_condition_types"
class="block appearance-none w-32 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none w-32 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option
v-for="option in conditionTypeOptions"
:key="option.value"
:value="option.value"
>{{ option.label }}
>
{{ option.label }}
</option>
</select>
<div
@@ -512,14 +508,15 @@
<select
v-model="editRuleObject.conditions[key].match"
id="edit_rule_condition_matches"
class="block appearance-none w-40 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none w-40 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option
v-for="option in conditionMatchOptions(editRuleObject, key)"
:key="option"
:value="option"
>{{ option }}
>
{{ option }}
</option>
</select>
<div
@@ -607,9 +604,7 @@
<div v-for="(action, key) in editRuleObject.actions" :key="key">
<!-- AND/OR operator -->
<div v-if="key !== 0" class="flex justify-center my-2">
<div class="relative">
AND
</div>
<div class="relative">AND</div>
</div>
<div class="p-2 w-full bg-grey-100">
@@ -622,14 +617,15 @@
v-model="editRuleObject.actions[key].type"
@change="ruleActionChange(editRuleObject.actions[key])"
id="rule_action_types"
class="block appearance-none text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option
v-for="option in actionTypeOptions"
:key="option.value"
:value="option.value"
>{{ option.label }}
>
{{ option.label }}
</option>
</select>
<div
@@ -651,7 +647,7 @@
<span
v-if="
editRuleObject.actions[key].type === 'subject' ||
editRuleObject.actions[key].type === 'displayFrom'
editRuleObject.actions[key].type === 'displayFrom'
"
class="ml-4 flex"
>
@@ -672,12 +668,12 @@
<select
v-model="editRuleObject.actions[key].value"
id="edit_rule_action_banner"
class="block appearance-none w-40 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:shadow-outline"
class="block appearance-none w-40 text-grey-700 bg-white p-2 pr-6 rounded shadow focus:ring"
required
>
<option value="top">Top </option>
<option value="bottom">Bottom </option>
<option value="off">Off </option>
<option value="top">Top</option>
<option value="bottom">Bottom</option>
<option value="off">Off</option>
</select>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
@@ -747,9 +743,7 @@
>
Delete rule
</h2>
<p class="mt-4 text-grey-700">
Are you sure you want to delete this rule?
</p>
<p class="mt-4 text-grey-700">Are you sure you want to delete this rule?</p>
<div class="mt-6">
<button
type="button"
@@ -776,7 +770,6 @@
<script>
import Modal from './../components/Modal.vue'
import Toggle from './../components/Toggle.vue'
import tippy from 'tippy.js'
import draggable from 'vuedraggable'
export default {
@@ -791,9 +784,6 @@ export default {
Toggle,
draggable,
},
mounted() {
this.addTooltips()
},
data() {
return {
editRuleObject: {},
@@ -869,11 +859,6 @@ export default {
errors: {},
}
},
watch: {
editRuleObject: _.debounce(function() {
this.addTooltips()
}, 50),
},
computed: {
activeRules() {
return _.filter(this.rows, rule => rule.active)
@@ -891,15 +876,6 @@ export default {
},
},
methods: {
addTooltips() {
tippy('.tooltip', {
arrow: true,
arrowType: 'round',
})
},
debounceToolips: _.debounce(function() {
this.addTooltips()
}, 50),
openCreateModal() {
this.errors = {}
this.createRuleModalOpen = true

View File

@@ -311,6 +311,9 @@
<script>
import Modal from './../components/Modal.vue'
import Toggle from './../components/Toggle.vue'
import { roundArrow } from 'tippy.js'
import 'tippy.js/dist/svg-arrow.css'
import 'tippy.js/dist/tippy.css'
import tippy from 'tippy.js'
import Multiselect from 'vue-multiselect'
@@ -334,9 +337,6 @@ export default {
Toggle,
Multiselect,
},
mounted() {
this.addTooltips()
},
data() {
return {
newUsername: '',
@@ -399,21 +399,26 @@ export default {
},
],
rows: this.initialUsernames,
tippyInstance: null,
}
},
watch: {
usernameIdToEdit: _.debounce(function() {
usernameIdToEdit: _.debounce(function () {
this.addTooltips()
}, 50),
},
methods: {
addTooltips() {
tippy('.tooltip', {
arrow: true,
arrowType: 'round',
if (this.tippyInstance) {
_.each(this.tippyInstance, instance => instance.destroy())
}
this.tippyInstance = tippy('.tooltip', {
arrow: roundArrow,
allowHTML: true,
})
},
debounceToolips: _.debounce(function() {
debounceToolips: _.debounce(function () {
this.addTooltips()
}, 50),
validateNewUsername(e) {

View File

@@ -27,7 +27,7 @@ function WebAuthn(notifyCallback = null) {
* @param {PublicKeyCredentialCreationOptions} publicKey - see https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialcreationoptions
* @param {function(PublicKeyCredential)} callback User callback
*/
WebAuthn.prototype.register = function(publicKey, callback) {
WebAuthn.prototype.register = function (publicKey, callback) {
let publicKeyCredential = Object.assign({}, publicKey)
publicKeyCredential.user.id = this._bufferDecode(publicKey.user.id)
publicKeyCredential.challenge = this._bufferDecode(this._base64Decode(publicKey.challenge))
@@ -56,7 +56,7 @@ WebAuthn.prototype.register = function(publicKey, callback) {
* @param {PublicKeyCredential} publicKey @see https://www.w3.org/TR/webauthn/#publickeycredential
* @param {function(PublicKeyCredential)} callback User callback
*/
WebAuthn.prototype._registerCallback = function(publicKey, callback) {
WebAuthn.prototype._registerCallback = function (publicKey, callback) {
let publicKeyCredential = {
id: publicKey.id,
type: publicKey.type,
@@ -77,7 +77,7 @@ WebAuthn.prototype._registerCallback = function(publicKey, callback) {
* @param {PublicKeyCredentialRequestOptions} publicKey - see https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrequestoptions
* @param {function(PublicKeyCredential)} callback User callback
*/
WebAuthn.prototype.sign = function(publicKey, callback) {
WebAuthn.prototype.sign = function (publicKey, callback) {
let publicKeyCredential = Object.assign({}, publicKey)
publicKeyCredential.challenge = this._bufferDecode(this._base64Decode(publicKey.challenge))
if (publicKey.allowCredentials) {
@@ -105,7 +105,7 @@ WebAuthn.prototype.sign = function(publicKey, callback) {
* @param {PublicKeyCredential} publicKey @see https://www.w3.org/TR/webauthn/#publickeycredential
* @param {function(PublicKeyCredential)} callback User callback
*/
WebAuthn.prototype._signCallback = function(publicKey, callback) {
WebAuthn.prototype._signCallback = function (publicKey, callback) {
let publicKeyCredential = {
id: publicKey.id,
type: publicKey.type,
@@ -130,7 +130,7 @@ WebAuthn.prototype._signCallback = function(publicKey, callback) {
* @param {ArrayBuffer} value
* @return {string}
*/
WebAuthn.prototype._bufferEncode = function(value) {
WebAuthn.prototype._bufferEncode = function (value) {
return window.btoa(String.fromCharCode.apply(null, new Uint8Array(value)))
}
@@ -140,7 +140,7 @@ WebAuthn.prototype._bufferEncode = function(value) {
* @param {ArrayBuffer} value
* @return {string}
*/
WebAuthn.prototype._bufferDecode = function(value) {
WebAuthn.prototype._bufferDecode = function (value) {
var t = window.atob(value)
return Uint8Array.from(t, c => c.charCodeAt(0))
}
@@ -151,7 +151,7 @@ WebAuthn.prototype._bufferDecode = function(value) {
* @param {string} input
* @return {string}
*/
WebAuthn.prototype._base64Decode = function(input) {
WebAuthn.prototype._base64Decode = function (input) {
// Replace non-url compatible chars with base64 standard chars
input = input.replace(/-/g, '+').replace(/_/g, '/')
@@ -175,9 +175,9 @@ WebAuthn.prototype._base64Decode = function(input) {
* @param {PublicKeyCredentialDescriptor} credentials
* @return {PublicKeyCredentialDescriptor}
*/
WebAuthn.prototype._credentialDecode = function(credentials) {
WebAuthn.prototype._credentialDecode = function (credentials) {
var self = this
return credentials.map(function(data) {
return credentials.map(function (data) {
return {
id: self._bufferDecode(self._base64Decode(data.id)),
type: data.type,
@@ -191,7 +191,7 @@ WebAuthn.prototype._credentialDecode = function(credentials) {
*
* @return {bool}
*/
WebAuthn.prototype.webAuthnSupport = function() {
WebAuthn.prototype.webAuthnSupport = function () {
return !(
window.PublicKeyCredential === undefined ||
typeof window.PublicKeyCredential !== 'function' ||
@@ -204,7 +204,7 @@ WebAuthn.prototype.webAuthnSupport = function() {
*
* @return {string}
*/
WebAuthn.prototype.notSupportedMessage = function() {
WebAuthn.prototype.notSupportedMessage = function () {
if (
!window.isSecureContext &&
window.location.hostname !== 'localhost' &&
@@ -221,7 +221,7 @@ WebAuthn.prototype.notSupportedMessage = function() {
* @param {string} message
* @param {bool} isError
*/
WebAuthn.prototype._notify = function(message, isError) {
WebAuthn.prototype._notify = function (message, isError) {
if (this._notifyCallback) {
this._notifyCallback(message, isError)
}
@@ -232,7 +232,7 @@ WebAuthn.prototype._notify = function(message, isError) {
*
* @param {function(name: string, message: string, isError: bool)} callback
*/
WebAuthn.prototype.setNotify = function(callback) {
WebAuthn.prototype.setNotify = function (callback) {
this._notifyCallback = callback
}

View File

@@ -27,7 +27,7 @@
Backup Code:
</label>
<input id="backup_code" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('backup_code') ? ' border border-red-500' : '' }}" name="backup_code" required autofocus>
<input id="backup_code" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('backup_code') ? ' border border-red-500' : '' }}" name="backup_code" required autofocus>
@if ($errors->has('backup_code'))
<p class="text-red-500 text-xs italic mt-4">

View File

@@ -30,7 +30,7 @@
{{ __('Username') }}:
</label>
<input id="username" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('username') ? ' border-red-500' : '' }}" name="username" value="{{ old('username') }}" placeholder="johndoe" required autofocus>
<input id="username" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('username') ? ' border-red-500' : '' }}" name="username" value="{{ old('username') }}" placeholder="johndoe" required autofocus>
@if ($errors->has('username'))
<p class="text-red-500 text-xs italic mt-4">
@@ -44,7 +44,7 @@
{{ __('Password') }}:
</label>
<input id="password" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('password') ? ' border-red-500' : '' }}" name="password" placeholder="********" required>
<input id="password" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('password') ? ' border-red-500' : '' }}" name="password" placeholder="********" required>
@if ($errors->has('password'))
<p class="text-red-500 text-xs italic mt-4">
@@ -61,7 +61,7 @@
</label>
</div>
@if (Route::has('password.request'))
<a class="whitespace-no-wrap no-underline text-sm" href="{{ route('password.request') }}">
<a class="whitespace-nowrap no-underline text-sm" href="{{ route('password.request') }}">
{{ __('Forgot Username/Password?') }}
</a>
@endif

View File

@@ -29,7 +29,7 @@
{{ __('Username') }}:
</label>
<input id="username" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('username') ? ' border-red-500' : '' }}" name="username" value="{{ old('username') }}" placeholder="johndoe" required>
<input id="username" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('username') ? ' border-red-500' : '' }}" name="username" value="{{ old('username') }}" placeholder="johndoe" required>
@if ($errors->has('username'))
<p class="text-red-500 text-xs italic mt-4">
@@ -38,7 +38,7 @@
@endif
</div>
<a class="whitespace-no-wrap no-underline text-sm" href="{{ route('username.reminder.show') }}">
<a class="whitespace-nowrap no-underline text-sm" href="{{ route('username.reminder.show') }}">
{{ __('Forgot Username?') }}
</a>

View File

@@ -25,7 +25,7 @@
{{ __('Username') }}:
</label>
<input id="username" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('username') ? ' border-red-500' : '' }}" name="username" value="{{ old('username') }}" placeholder="johndoe" required autofocus>
<input id="username" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('username') ? ' border-red-500' : '' }}" name="username" value="{{ old('username') }}" placeholder="johndoe" required autofocus>
@if ($errors->has('username'))
<p class="text-red-500 text-xs italic mt-4">
@@ -39,7 +39,7 @@
{{ __('New Password') }}:
</label>
<input id="password" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('password') ? ' border-red-500' : '' }}" name="password" placeholder="********" required>
<input id="password" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('password') ? ' border-red-500' : '' }}" name="password" placeholder="********" required>
@if ($errors->has('password'))
<p class="text-red-500 text-xs italic mt-4">
@@ -53,7 +53,7 @@
{{ __('Confirm New Password') }}:
</label>
<input id="password-confirm" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline" name="password_confirmation" placeholder="********" required>
<input id="password-confirm" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring" name="password_confirmation" placeholder="********" required>
</div>
</div>

View File

@@ -24,7 +24,7 @@
</label>
<div class="table w-full">
<input id="username" type="text" class="table-cell relative appearance-none bg-grey-100 rounded-l w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('username') ? ' border-red-500' : '' }}" name="username" value="{{ old('username') }}" placeholder="johndoe" required autofocus>
<input id="username" type="text" class="table-cell relative appearance-none bg-grey-100 rounded-l w-full p-3 text-grey-700 focus:ring{{ $errors->has('username') ? ' border-red-500' : '' }}" name="username" value="{{ old('username') }}" placeholder="johndoe" required autofocus>
<div class="py-3 px-2 table-cell align-middle bg-grey-200 rounded-r text-grey-600">
.{{ config('anonaddy.domain') }}
</div>
@@ -45,7 +45,7 @@
Your Real Email Address:
</label>
<input id="email" type="email" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('email') ? ' border-red-500' : '' }}" name="email" value="{{ old('email') }}" placeholder="johndoe@example.com" required>
<input id="email" type="email" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('email') ? ' border-red-500' : '' }}" name="email" value="{{ old('email') }}" placeholder="johndoe@example.com" required>
<p class="text-xs mt-1 text-grey-600">This is your recipient where emails will be forwarded</p>
@@ -61,7 +61,7 @@
Confirm Email Address:
</label>
<input id="email-confirm" type="email" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline" name="email_confirmation" value="{{ old('email_confirmation') }}" placeholder="johndoe@example.com" required>
<input id="email-confirm" type="email" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring" name="email_confirmation" value="{{ old('email_confirmation') }}" placeholder="johndoe@example.com" required>
</div>
<div class="flex flex-wrap mb-6">
@@ -69,7 +69,7 @@
{{ __('Password') }}:
</label>
<input id="password" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('password') ? ' border-red-500' : '' }}" name="password" placeholder="********" required>
<input id="password" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('password') ? ' border-red-500' : '' }}" name="password" placeholder="********" required>
@if ($errors->has('password'))
<p class="text-red-500 text-xs italic mt-4">
@@ -86,7 +86,7 @@
<div class="flex flex-grow flex-wrap">
<img src="{{captcha_src('mini')}}" class="flex-shrink-0 h-12 w-16 mr-2 mt-2">
<input id="captcha" type="text" class="flex-grow mt-2 appearance-none bg-grey-100 rounded p-3 text-grey-700 focus:shadow-outline{{ $errors->has('captcha') ? ' border-red-500' : '' }}" name="captcha" placeholder="Enter the text you see" required>
<input id="captcha" type="text" class="flex-grow mt-2 appearance-none bg-grey-100 rounded p-3 text-grey-700 focus:ring{{ $errors->has('captcha') ? ' border-red-500' : '' }}" name="captcha" placeholder="Enter the text you see" required>
</div>
@if ($errors->has('captcha'))

View File

@@ -29,7 +29,7 @@
{{ __('One Time Token') }}:
</label>
<input id="one_time_password" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('message') ? ' border border-red-500' : '' }}" name="one_time_password" placeholder="123456" required autofocus>
<input id="one_time_password" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('message') ? ' border border-red-500' : '' }}" name="one_time_password" placeholder="123456" required autofocus>
@if ($errors->has('message'))
<p class="text-red-500 text-xs italic mt-4">

View File

@@ -29,7 +29,7 @@
{{ __('Email') }}:
</label>
<input id="email" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('email') ? ' border-red-500' : '' }}" name="email" value="{{ old('email') }}" placeholder="johndoe@example.com" required>
<input id="email" type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('email') ? ' border-red-500' : '' }}" name="email" value="{{ old('email') }}" placeholder="johndoe@example.com" required>
@if ($errors->has('email'))
<p class="text-red-500 text-xs italic mt-4">
@@ -39,7 +39,7 @@
</div>
@if (Route::has('password.request'))
<a class="whitespace-no-wrap no-underline text-sm" href="{{ route('password.request') }}">
<a class="whitespace-nowrap no-underline text-sm" href="{{ route('password.request') }}">
{{ __('Forgot Password?') }}
</a>
@endif

View File

@@ -26,7 +26,7 @@
<form method="POST" action="{{ route('verification.resend') }}" class="w-full">
@csrf
<button type="submit" class="bg-cyan-400 w-full text-center hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:shadow-outline no-underline mx-auto">
<button type="submit" class="bg-cyan-400 w-full text-center hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:ring no-underline mx-auto">
{{ __('Resend verification email') }}
</button>
</form>

View File

@@ -70,7 +70,7 @@
</label>
<div class="block relative w-full">
<select id="default-recipient" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="default_recipient" required>
<select id="default-recipient" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="default_recipient" required>
@foreach($recipientOptions as $recipient)
<option value="{{ $recipient->id }}" {{ $user->email === $recipient->email ? 'selected' : '' }}>{{ $recipient->email }}</option>
@endforeach
@@ -116,7 +116,7 @@
</label>
<div class="block relative w-full">
<input id="email" type="email" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="email" value="{{ old('email') ?? $user->email }}">
<input id="email" type="email" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="email" value="{{ old('email') ?? $user->email }}">
</div>
@if ($errors->has('email'))
@@ -132,7 +132,7 @@
</label>
<div class="block relative w-full">
<input id="email_confirmation" type="email" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="email_confirmation">
<input id="email_confirmation" type="email" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="email_confirmation">
</div>
</div>
@@ -165,7 +165,7 @@
</label>
<div class="block relative w-full">
<select id="default-alias-domain" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="domain" required>
<select id="default-alias-domain" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="domain" required>
@foreach($user->domainOptions() as $domainOption)
<option value="{{ $domainOption }}" {{ $user->default_alias_domain === $domainOption ? 'selected' : '' }}>{{ $domainOption }}</option>
@endforeach
@@ -209,7 +209,7 @@
</label>
<div class="block relative w-full">
<select id="default-alias-format" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="format" required>
<select id="default-alias-format" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="format" required>
<option value="uuid" {{ $user->default_alias_format === 'uuid' ? 'selected' : '' }}>UUID</option>
<option value="random_words" {{ $user->default_alias_format === 'random_words' ? 'selected' : '' }}>Random Words</option>
<option value="custom" {{ $user->default_alias_format === 'custom' ? 'selected' : '' }}>Custom</option>
@@ -250,7 +250,7 @@
{{ __('Current Password') }}:
</label>
<input id="current" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('current') ? ' border-red-500' : '' }}" name="current" placeholder="********" required>
<input id="current" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('current') ? ' border-red-500' : '' }}" name="current" placeholder="********" required>
@if ($errors->has('current'))
<p class="text-red-500 text-xs italic mt-4">
@@ -264,7 +264,7 @@
{{ __('New Password') }}:
</label>
<input id="password" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('password') ? ' border-red-500' : '' }}" name="password" placeholder="********" required>
<input id="password" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('password') ? ' border-red-500' : '' }}" name="password" placeholder="********" required>
@if ($errors->has('password'))
<p class="text-red-500 text-xs italic mt-4">
@@ -278,7 +278,7 @@
{{ __('Confirm New Password') }}:
</label>
<input id="password-confirm" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline" name="password_confirmation" placeholder="********" required>
<input id="password-confirm" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring" name="password_confirmation" placeholder="********" required>
</div>
</div>
@@ -336,7 +336,7 @@
{{ __('Current Password') }}:
</label>
<input id="current_password_2fa" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline{{ $errors->has('current_password_2fa') ? ' border-red-500' : '' }}" name="current_password_2fa" placeholder="********" required>
<input id="current_password_2fa" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring{{ $errors->has('current_password_2fa') ? ' border-red-500' : '' }}" name="current_password_2fa" placeholder="********" required>
@if ($errors->has('current_password_2fa'))
<p class="text-red-500 text-xs italic mt-4">
@@ -396,7 +396,7 @@
</label>
<div class="block relative w-full">
<input id="two_factor_token" type="text" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="two_factor_token" placeholder="123456" />
<input id="two_factor_token" type="text" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="two_factor_token" placeholder="123456" />
</div>
@if ($errors->has('two_factor_token'))
@@ -466,7 +466,7 @@
</label>
<div class="block relative w-full">
<select id="catch_all" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="catch_all" required>
<select id="catch_all" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="catch_all" required>
<option value="1" {{ $user->catch_all ? 'selected' : '' }}>Enabled</option>
<option value="0" {{ ! $user->catch_all ? 'selected' : '' }}>Disabled</option>
</select>
@@ -509,7 +509,7 @@
</label>
<div class="block relative w-full">
<input id="from_name" type="text" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="from_name" value="{{ $user->from_name }}" placeholder="John Doe" />
<input id="from_name" type="text" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="from_name" value="{{ $user->from_name }}" placeholder="John Doe" />
</div>
@if ($errors->has('from_name'))
@@ -546,7 +546,7 @@
</label>
<div class="block relative w-full">
<select id="banner_location" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="banner_location" required>
<select id="banner_location" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="banner_location" required>
<option value="top" {{ $user->banner_location === 'top' ? 'selected' : '' }}>Top</option>
<option value="bottom" {{ $user->banner_location === 'bottom' ? 'selected' : '' }}>Bottom</option>
<option value="off" {{ $user->banner_location === 'off' ? 'selected' : '' }}>Off</option>
@@ -592,7 +592,7 @@
</label>
<div class="block relative w-full">
<input id="email_subject" type="text" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:shadow-outline" name="email_subject" value="{{ $user->email_subject }}" placeholder="The subject" />
<input id="email_subject" type="text" class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring" name="email_subject" value="{{ $user->email_subject }}" placeholder="The subject" />
</div>
@if ($errors->has('email_subject'))
@@ -677,7 +677,7 @@
{{ __('Enter your password to continue') }}:
</label>
<input id="current-password-delete" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:outline-none focus:shadow-outline{{ $errors->has('current_password_delete') ? ' border-red-500' : '' }}" name="current_password_delete" placeholder="********" required>
<input id="current-password-delete" type="password" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:outline-none focus:ring{{ $errors->has('current_password_delete') ? ' border-red-500' : '' }}" name="current_password_delete" placeholder="********" required>
@if ($errors->has('current_password_delete'))
<p class="text-red-500 text-xs italic mt-4">

View File

@@ -40,7 +40,7 @@
<label for="name" class="block text-grey-700 text-sm mb-2">
Name:
</label>
<input type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:shadow-outline" name="name" id="name" placeholder="Yubikey" required autofocus>
<input type="text" class="appearance-none bg-grey-100 rounded w-full p-3 text-grey-700 focus:ring" name="name" id="name" placeholder="Yubikey" required autofocus>
@if ($errors->has('name'))
<p class="text-red-500 text-xs italic mt-4">

26
tailwind.config.js vendored
View File

@@ -1,8 +1,4 @@
module.exports = {
future: {
removeDeprecatedGapUtilities: true,
purgeLayersByDefault: true,
},
purge: {
content: [
'app/**/*.php',
@@ -14,7 +10,15 @@ module.exports = {
// These options are passed through directly to PurgeCSS
options: {
whitelistPatterns: [/-active$/, /-enter$/, /-leave-to$/, /show$/],
safelist: [
/-active$/,
/-enter$/,
/-leave-to$/,
/show$/,
'bg-green-400',
'bg-red-400',
'bg-grey-400',
],
},
},
theme: {
@@ -107,6 +111,18 @@ module.exports = {
900: '#014807',
},
},
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
base: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
'3xl': '1.875rem',
'4xl': '2.25rem',
'5xl': '3rem',
'6xl': '4rem',
},
container: {
center: true,
padding: '1.5rem',

9
webpack.mix.js vendored
View File

@@ -3,14 +3,7 @@ let mix = require('laravel-mix')
mix
.js('resources/js/app.js', 'public/js')
.js('resources/js/webauthn.js', 'public/js')
.postCss('resources/css/app.css', 'public/css')
.options({
postCss: [
require('postcss-import')(),
require('tailwindcss')('./tailwind.config.js'),
require('postcss-nesting')(),
],
})
.postCss('resources/css/app.css', 'public/css', [require('tailwindcss')])
if (mix.inProduction()) {
mix.version()