|
|
|
@ -1,12 +1,12 @@ |
|
|
|
<script> |
|
|
|
import { ref, reactive } from "vue"; |
|
|
|
import { ref, reactive } from 'vue'; |
|
|
|
|
|
|
|
class API { |
|
|
|
async call({ method, path, body }) { |
|
|
|
const res = await fetch(`./api${path}`, { |
|
|
|
method, |
|
|
|
headers: { |
|
|
|
"Content-Type": "application/json", |
|
|
|
'Content-Type': 'application/json', |
|
|
|
}, |
|
|
|
body: body ? JSON.stringify(body) : undefined, |
|
|
|
}); |
|
|
|
@ -26,79 +26,82 @@ class API { |
|
|
|
|
|
|
|
async getRelease() { |
|
|
|
return this.call({ |
|
|
|
method: "get", |
|
|
|
path: "/release", |
|
|
|
method: 'get', |
|
|
|
path: '/release', |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async getSession() { |
|
|
|
return this.call({ |
|
|
|
method: "get", |
|
|
|
path: "/session", |
|
|
|
method: 'get', |
|
|
|
path: '/session', |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async createSession({ password }) { |
|
|
|
return this.call({ |
|
|
|
method: "post", |
|
|
|
path: "/session", |
|
|
|
method: 'post', |
|
|
|
path: '/session', |
|
|
|
body: { password }, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async deleteSession() { |
|
|
|
return this.call({ |
|
|
|
method: "delete", |
|
|
|
path: "/session", |
|
|
|
method: 'delete', |
|
|
|
path: '/session', |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async getClients() { |
|
|
|
return this.call({ |
|
|
|
method: "get", |
|
|
|
path: "/wireguard/client", |
|
|
|
method: 'get', |
|
|
|
path: '/wireguard/client', |
|
|
|
}).then((clients) => |
|
|
|
clients.map((client) => ({ |
|
|
|
...client, |
|
|
|
createdAt: new Date(client.createdAt), |
|
|
|
updatedAt: new Date(client.updatedAt), |
|
|
|
latestHandshakeAt: client.latestHandshakeAt !== null ? new Date(client.latestHandshakeAt) : null, |
|
|
|
latestHandshakeAt: |
|
|
|
client.latestHandshakeAt !== null |
|
|
|
? new Date(client.latestHandshakeAt) |
|
|
|
: null, |
|
|
|
})) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
async createClient({ name }) { |
|
|
|
return this.call({ |
|
|
|
method: "post", |
|
|
|
path: "/wireguard/client", |
|
|
|
method: 'post', |
|
|
|
path: '/wireguard/client', |
|
|
|
body: { name }, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async deleteClient({ clientId }) { |
|
|
|
return this.call({ |
|
|
|
method: "delete", |
|
|
|
method: 'delete', |
|
|
|
path: `/wireguard/client/${clientId}`, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async enableClient({ clientId }) { |
|
|
|
return this.call({ |
|
|
|
method: "post", |
|
|
|
method: 'post', |
|
|
|
path: `/wireguard/client/${clientId}/enable`, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async disableClient({ clientId }) { |
|
|
|
return this.call({ |
|
|
|
method: "post", |
|
|
|
method: 'post', |
|
|
|
path: `/wireguard/client/${clientId}/disable`, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async updateClientName({ clientId, name }) { |
|
|
|
return this.call({ |
|
|
|
method: "put", |
|
|
|
method: 'put', |
|
|
|
path: `/wireguard/client/${clientId}/name/`, |
|
|
|
body: { name }, |
|
|
|
}); |
|
|
|
@ -106,7 +109,7 @@ class API { |
|
|
|
|
|
|
|
async updateClientAddress({ clientId, address }) { |
|
|
|
return this.call({ |
|
|
|
method: "put", |
|
|
|
method: 'put', |
|
|
|
path: `/wireguard/client/${clientId}/address/`, |
|
|
|
body: { address }, |
|
|
|
}); |
|
|
|
@ -124,7 +127,7 @@ export default { |
|
|
|
const clientsPersist = reactive({}); |
|
|
|
const clientDelete = ref(null); |
|
|
|
const clientCreate = ref(null); |
|
|
|
const clientCreateName = ref(""); |
|
|
|
const clientCreateName = ref(''); |
|
|
|
const clientEditName = ref(null); |
|
|
|
const clientEditNameId = ref(null); |
|
|
|
const clientEditAddress = ref(null); |
|
|
|
@ -138,8 +141,8 @@ export default { |
|
|
|
|
|
|
|
const chartOptions = reactive({ |
|
|
|
chart: { |
|
|
|
background: "transparent", |
|
|
|
type: "bar", |
|
|
|
background: 'transparent', |
|
|
|
type: 'bar', |
|
|
|
stacked: false, |
|
|
|
toolbar: { |
|
|
|
show: false, |
|
|
|
@ -149,8 +152,8 @@ export default { |
|
|
|
}, |
|
|
|
}, |
|
|
|
colors: [ |
|
|
|
"#DDDDDD", // rx |
|
|
|
"#EEEEEE", // tx |
|
|
|
'#DDDDDD', // rx |
|
|
|
'#EEEEEE', // tx |
|
|
|
], |
|
|
|
dataLabels: { |
|
|
|
enabled: false, |
|
|
|
@ -206,11 +209,11 @@ export default { |
|
|
|
|
|
|
|
const dateTime = (value) => { |
|
|
|
return new Intl.DateTimeFormat(undefined, { |
|
|
|
year: "numeric", |
|
|
|
month: "short", |
|
|
|
day: "numeric", |
|
|
|
hour: "numeric", |
|
|
|
minute: "numeric", |
|
|
|
year: 'numeric', |
|
|
|
month: 'short', |
|
|
|
day: 'numeric', |
|
|
|
hour: 'numeric', |
|
|
|
minute: 'numeric', |
|
|
|
}).format(value); |
|
|
|
}; |
|
|
|
|
|
|
|
@ -219,9 +222,11 @@ export default { |
|
|
|
|
|
|
|
const clientsData = await api.getClients(); |
|
|
|
clients.value = clientsData.map((client) => { |
|
|
|
if (client.name.includes("@") && client.name.includes(".")) { |
|
|
|
if (client.name.includes('@') && client.name.includes('.')) { |
|
|
|
// FIXME: add "sha512" |
|
|
|
client.avatar = `https://www.gravatar.com/avatar/${sha512(client.name)}?d=blank`; |
|
|
|
client.avatar = `https://www.gravatar.com/avatar/${sha512( |
|
|
|
client.name |
|
|
|
)}?d=blank`; |
|
|
|
} |
|
|
|
|
|
|
|
if (!clientsPersist[client.id]) { |
|
|
|
@ -244,10 +249,14 @@ export default { |
|
|
|
client.transferTx - clientsPersist[client.id].transferTxPrevious; |
|
|
|
clientsPersist[client.id].transferTxPrevious = client.transferTx; |
|
|
|
|
|
|
|
clientsPersist[client.id].transferRxHistory.push(clientsPersist[client.id].transferRxCurrent); |
|
|
|
clientsPersist[client.id].transferRxHistory.push( |
|
|
|
clientsPersist[client.id].transferRxCurrent |
|
|
|
); |
|
|
|
clientsPersist[client.id].transferRxHistory.shift(); |
|
|
|
|
|
|
|
clientsPersist[client.id].transferTxHistory.push(clientsPersist[client.id].transferTxCurrent); |
|
|
|
clientsPersist[client.id].transferTxHistory.push( |
|
|
|
clientsPersist[client.id].transferTxCurrent |
|
|
|
); |
|
|
|
clientsPersist[client.id].transferTxHistory.shift(); |
|
|
|
} |
|
|
|
|
|
|
|
@ -256,7 +265,10 @@ export default { |
|
|
|
|
|
|
|
client.transferTxHistory = clientsPersist[client.id].transferTxHistory; |
|
|
|
client.transferRxHistory = clientsPersist[client.id].transferRxHistory; |
|
|
|
client.transferMax = Math.max(...client.transferTxHistory, ...client.transferRxHistory); |
|
|
|
client.transferMax = Math.max( |
|
|
|
...client.transferTxHistory, |
|
|
|
...client.transferRxHistory |
|
|
|
); |
|
|
|
|
|
|
|
client.hoverTx = clientsPersist[client.id].hoverTx; |
|
|
|
client.hoverRx = clientsPersist[client.id].hoverRx; |
|
|
|
@ -272,7 +284,8 @@ export default { |
|
|
|
if (authenticating.value) return; |
|
|
|
|
|
|
|
authenticating.value = true; |
|
|
|
api.createSession({ |
|
|
|
api |
|
|
|
.createSession({ |
|
|
|
password: password.value, |
|
|
|
}) |
|
|
|
.then(async () => { |
|
|
|
@ -293,7 +306,8 @@ export default { |
|
|
|
const logout = (e) => { |
|
|
|
e.preventDefault(); |
|
|
|
|
|
|
|
api.deleteSession() |
|
|
|
api |
|
|
|
.deleteSession() |
|
|
|
.then(() => { |
|
|
|
authenticated.value = false; |
|
|
|
clients.value = null; |
|
|
|
@ -307,48 +321,54 @@ export default { |
|
|
|
const name = clientCreateName.value; |
|
|
|
if (!name) return; |
|
|
|
|
|
|
|
api.createClient({ name }) |
|
|
|
api |
|
|
|
.createClient({ name }) |
|
|
|
.catch((err) => alert(err.message || err.toString())) |
|
|
|
.finally(() => refresh().catch(console.error)); |
|
|
|
}; |
|
|
|
|
|
|
|
const deleteClient = (client) => { |
|
|
|
api.deleteClient({ clientId: client.id }) |
|
|
|
api |
|
|
|
.deleteClient({ clientId: client.id }) |
|
|
|
.catch((err) => alert(err.message || err.toString())) |
|
|
|
.finally(() => refresh().catch(console.error)); |
|
|
|
}; |
|
|
|
|
|
|
|
const enableClient = (client) => { |
|
|
|
api.enableClient({ clientId: client.id }) |
|
|
|
api |
|
|
|
.enableClient({ clientId: client.id }) |
|
|
|
.catch((err) => alert(err.message || err.toString())) |
|
|
|
.finally(() => refresh().catch(console.error)); |
|
|
|
}; |
|
|
|
|
|
|
|
const disableClient = (client) => { |
|
|
|
api.disableClient({ clientId: client.id }) |
|
|
|
api |
|
|
|
.disableClient({ clientId: client.id }) |
|
|
|
.catch((err) => alert(err.message || err.toString())) |
|
|
|
.finally(() => refresh().catch(console.error)); |
|
|
|
}; |
|
|
|
|
|
|
|
const updateClientName = (client, name) => { |
|
|
|
api.updateClientName({ clientId: client.id, name }) |
|
|
|
api |
|
|
|
.updateClientName({ clientId: client.id, name }) |
|
|
|
.catch((err) => alert(err.message || err.toString())) |
|
|
|
.finally(() => refresh().catch(console.error)); |
|
|
|
}; |
|
|
|
|
|
|
|
const updateClientAddress = (client, address) => { |
|
|
|
api.updateClientAddress({ clientId: client.id, address }) |
|
|
|
api |
|
|
|
.updateClientAddress({ clientId: client.id, address }) |
|
|
|
.catch((err) => alert(err.message || err.toString())) |
|
|
|
.finally(() => refresh().catch(console.error)); |
|
|
|
}; |
|
|
|
|
|
|
|
const toggleTheme = () => { |
|
|
|
if (isDark.value) { |
|
|
|
localStorage.theme = "light"; |
|
|
|
document.documentElement.classList.remove("dark"); |
|
|
|
localStorage.theme = 'light'; |
|
|
|
document.documentElement.classList.remove('dark'); |
|
|
|
} else { |
|
|
|
localStorage.theme = "dark"; |
|
|
|
document.documentElement.classList.add("dark"); |
|
|
|
localStorage.theme = 'dark'; |
|
|
|
document.documentElement.classList.add('dark'); |
|
|
|
} |
|
|
|
isDark.value = !isDark.value; |
|
|
|
}; |
|
|
|
@ -394,7 +414,7 @@ export default { |
|
|
|
}, |
|
|
|
mounted() { |
|
|
|
this.isDark = false; |
|
|
|
if (localStorage.theme === "dark") { |
|
|
|
if (localStorage.theme === 'dark') { |
|
|
|
this.isDark = true; |
|
|
|
} |
|
|
|
|
|
|
|
@ -423,13 +443,17 @@ export default { |
|
|
|
Promise.resolve() |
|
|
|
.then(async () => { |
|
|
|
const currentReleaseData = await this.api.getRelease(); |
|
|
|
const latestReleaseData = await fetch("https://wg-easy.github.io/wg-easy/changelog.json") |
|
|
|
const latestReleaseData = await fetch( |
|
|
|
'https://wg-easy.github.io/wg-easy/changelog.json' |
|
|
|
) |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((releases) => { |
|
|
|
const releasesArray = Object.entries(releases).map(([version, changelog]) => ({ |
|
|
|
const releasesArray = Object.entries(releases).map( |
|
|
|
([version, changelog]) => ({ |
|
|
|
version: parseInt(version, 10), |
|
|
|
changelog, |
|
|
|
})); |
|
|
|
}) |
|
|
|
); |
|
|
|
releasesArray.sort((a, b) => { |
|
|
|
return b.version - a.version; |
|
|
|
}); |
|
|
|
@ -484,7 +508,11 @@ export default { |
|
|
|
</svg> |
|
|
|
</span> |
|
|
|
<h1 class="text-4xl dark:text-neutral-200 font-medium mt-2 mb-2"> |
|
|
|
<img src="./assets/img/logo.png" width="32" class="inline align-middle dark:bg" /> |
|
|
|
<img |
|
|
|
src="./assets/img/logo.png" |
|
|
|
width="32" |
|
|
|
class="inline align-middle dark:bg" |
|
|
|
/> |
|
|
|
<span class="align-middle">WireGuard</span> |
|
|
|
</h1> |
|
|
|
<h2 class="text-sm text-gray-400 dark:text-neutral-400 mb-10"></h2> |
|
|
|
@ -510,7 +538,9 @@ export default { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="shadow-md rounded-lg bg-white dark:bg-neutral-700 overflow-hidden"> |
|
|
|
<div |
|
|
|
class="shadow-md rounded-lg bg-white dark:bg-neutral-700 overflow-hidden" |
|
|
|
> |
|
|
|
<div |
|
|
|
class="flex flex-row flex-auto items-center p-3 px-5 border-b-2 border-gray-100 dark:border-neutral-600" |
|
|
|
> |
|
|
|
@ -575,7 +605,9 @@ export default { |
|
|
|
</apexchart> |
|
|
|
</div> --> |
|
|
|
|
|
|
|
<div class="relative p-5 z-10 flex flex-col md:flex-row justify-between"> |
|
|
|
<div |
|
|
|
class="relative p-5 z-10 flex flex-col md:flex-row justify-between" |
|
|
|
> |
|
|
|
<div class="flex items-center pb-2 md:pb-0"> |
|
|
|
<div class="h-10 w-10 mr-5 rounded-full bg-gray-50 relative"> |
|
|
|
<svg |
|
|
|
@ -599,7 +631,8 @@ export default { |
|
|
|
<div |
|
|
|
v-if=" |
|
|
|
client.latestHandshakeAt && |
|
|
|
new Date() - new Date(client.latestHandshakeAt) < 1000 * 60 * 10 |
|
|
|
new Date() - new Date(client.latestHandshakeAt) < |
|
|
|
1000 * 60 * 10 |
|
|
|
" |
|
|
|
> |
|
|
|
<div |
|
|
|
@ -615,7 +648,9 @@ export default { |
|
|
|
<!-- Name --> |
|
|
|
<div |
|
|
|
class="text-gray-700 dark:text-neutral-200 group" |
|
|
|
:title="'Created on ' + dateTime(new Date(client.createdAt))" |
|
|
|
:title=" |
|
|
|
'Created on ' + dateTime(new Date(client.createdAt)) |
|
|
|
" |
|
|
|
> |
|
|
|
<!-- Show --> |
|
|
|
<input |
|
|
|
@ -646,7 +681,10 @@ export default { |
|
|
|
clientEditName = client.name; |
|
|
|
clientEditNameId = client.id; |
|
|
|
setTimeout( |
|
|
|
() => $refs['client-' + client.id + '-name'][0].select(), |
|
|
|
() => |
|
|
|
$refs[ |
|
|
|
'client-' + client.id + '-name' |
|
|
|
][0].select(), |
|
|
|
1 |
|
|
|
); |
|
|
|
" |
|
|
|
@ -702,7 +740,10 @@ export default { |
|
|
|
clientEditAddress = client.address; |
|
|
|
clientEditAddressId = client.id; |
|
|
|
setTimeout( |
|
|
|
() => $refs['client-' + client.id + '-address'][0].select(), |
|
|
|
() => |
|
|
|
$refs[ |
|
|
|
'client-' + client.id + '-address' |
|
|
|
][0].select(), |
|
|
|
1 |
|
|
|
); |
|
|
|
" |
|
|
|
@ -774,7 +815,10 @@ export default { |
|
|
|
<!-- Last seen --> |
|
|
|
<span |
|
|
|
v-if="client.latestHandshakeAt" |
|
|
|
:title="'Last seen on ' + dateTime(new Date(client.latestHandshakeAt))" |
|
|
|
:title=" |
|
|
|
'Last seen on ' + |
|
|
|
dateTime(new Date(client.latestHandshakeAt)) |
|
|
|
" |
|
|
|
> |
|
|
|
<!-- FIXME: add "timeago" --> |
|
|
|
<!-- · {{ new Date(client.latestHandshakeAt) | timeago }} --> |
|
|
|
@ -810,7 +854,9 @@ export default { |
|
|
|
<button |
|
|
|
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition" |
|
|
|
title="Show QR Code" |
|
|
|
@click="qrcode = `./api/wireguard/client/${client.id}/qrcode.svg`" |
|
|
|
@click=" |
|
|
|
qrcode = `./api/wireguard/client/${client.id}/qrcode.svg` |
|
|
|
" |
|
|
|
> |
|
|
|
<svg |
|
|
|
class="w-5" |
|
|
|
@ -830,7 +876,9 @@ export default { |
|
|
|
|
|
|
|
<!-- Download Config --> |
|
|
|
<a |
|
|
|
:href="'./api/wireguard/client/' + client.id + '/configuration'" |
|
|
|
:href=" |
|
|
|
'./api/wireguard/client/' + client.id + '/configuration' |
|
|
|
" |
|
|
|
download |
|
|
|
class="align-middle inline-block bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition" |
|
|
|
title="Download Configuration" |
|
|
|
@ -875,7 +923,9 @@ export default { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div v-if="clients && clients.length === 0"> |
|
|
|
<p class="text-center m-10 text-gray-400 dark:text-neutral-400 text-sm"> |
|
|
|
<p |
|
|
|
class="text-center m-10 text-gray-400 dark:text-neutral-400 text-sm" |
|
|
|
> |
|
|
|
There are no clients yet.<br /><br /> |
|
|
|
<button |
|
|
|
@click=" |
|
|
|
@ -903,7 +953,10 @@ export default { |
|
|
|
</button> |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
<div v-if="clients === null" class="text-gray-200 dark:text-red-300 p-5"> |
|
|
|
<div |
|
|
|
v-if="clients === null" |
|
|
|
class="text-gray-200 dark:text-red-300 p-5" |
|
|
|
> |
|
|
|
<svg |
|
|
|
class="w-5 animate-spin mx-auto" |
|
|
|
xmlns="http://www.w3.org/2000/svg" |
|
|
|
@ -974,11 +1027,15 @@ export default { |
|
|
|
To: "opacity-0" |
|
|
|
--> |
|
|
|
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|
|
|
<div class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50"></div> |
|
|
|
<div |
|
|
|
class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50" |
|
|
|
></div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- This element is to trick the browser into centering the modal contents. --> |
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true" |
|
|
|
<span |
|
|
|
class="hidden sm:inline-block sm:align-middle sm:h-screen" |
|
|
|
aria-hidden="true" |
|
|
|
>​</span |
|
|
|
> |
|
|
|
<!-- |
|
|
|
@ -997,7 +1054,9 @@ export default { |
|
|
|
aria-modal="true" |
|
|
|
aria-labelledby="modal-headline" |
|
|
|
> |
|
|
|
<div class="bg-white dark:bg-neutral-700 px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
|
|
|
<div |
|
|
|
class="bg-white dark:bg-neutral-700 px-4 pt-5 pb-4 sm:p-6 sm:pb-4" |
|
|
|
> |
|
|
|
<div class="sm:flex sm:items-start"> |
|
|
|
<div |
|
|
|
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-800 sm:mx-0 sm:h-10 sm:w-10" |
|
|
|
@ -1018,7 +1077,9 @@ export default { |
|
|
|
/> |
|
|
|
</svg> |
|
|
|
</div> |
|
|
|
<div class="flex-grow mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> |
|
|
|
<div |
|
|
|
class="flex-grow mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left" |
|
|
|
> |
|
|
|
<h3 |
|
|
|
class="text-lg leading-6 font-medium text-gray-900 dark:text-neutral-200" |
|
|
|
id="modal-headline" |
|
|
|
@ -1038,7 +1099,9 @@ export default { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="bg-gray-50 dark:bg-neutral-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> |
|
|
|
<div |
|
|
|
class="bg-gray-50 dark:bg-neutral-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse" |
|
|
|
> |
|
|
|
<button |
|
|
|
v-if="clientCreateName.length" |
|
|
|
type="button" |
|
|
|
@ -1085,11 +1148,15 @@ export default { |
|
|
|
To: "opacity-0" |
|
|
|
--> |
|
|
|
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|
|
|
<div class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50"></div> |
|
|
|
<div |
|
|
|
class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50" |
|
|
|
></div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- This element is to trick the browser into centering the modal contents. --> |
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true" |
|
|
|
<span |
|
|
|
class="hidden sm:inline-block sm:align-middle sm:h-screen" |
|
|
|
aria-hidden="true" |
|
|
|
>​</span |
|
|
|
> |
|
|
|
<!-- |
|
|
|
@ -1108,7 +1175,9 @@ export default { |
|
|
|
aria-modal="true" |
|
|
|
aria-labelledby="modal-headline" |
|
|
|
> |
|
|
|
<div class="bg-white dark:bg-neutral-700 px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
|
|
|
<div |
|
|
|
class="bg-white dark:bg-neutral-700 px-4 pt-5 pb-4 sm:p-6 sm:pb-4" |
|
|
|
> |
|
|
|
<div class="sm:flex sm:items-start"> |
|
|
|
<div |
|
|
|
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10" |
|
|
|
@ -1139,14 +1208,17 @@ export default { |
|
|
|
</h3> |
|
|
|
<div class="mt-2"> |
|
|
|
<p class="text-sm text-gray-500 dark:text-neutral-300"> |
|
|
|
Are you sure you want to delete <strong>{{ clientDelete.name }}</strong |
|
|
|
Are you sure you want to delete |
|
|
|
<strong>{{ clientDelete.name }}</strong |
|
|
|
>? This action cannot be undone. |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="bg-gray-50 dark:bg-neutral-600 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> |
|
|
|
<div |
|
|
|
class="bg-gray-50 dark:bg-neutral-600 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse" |
|
|
|
> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
@click=" |
|
|
|
@ -1171,7 +1243,11 @@ export default { |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-if="authenticated === false"> |
|
|
|
<h1 class="text-4xl font-medium my-16 text-gray-700 dark:text-neutral-200 text-center">WireGuard</h1> |
|
|
|
<h1 |
|
|
|
class="text-4xl font-medium my-16 text-gray-700 dark:text-neutral-200 text-center" |
|
|
|
> |
|
|
|
WireGuard |
|
|
|
</h1> |
|
|
|
|
|
|
|
<form |
|
|
|
@submit="login" |
|
|
|
@ -1243,14 +1319,24 @@ export default { |
|
|
|
</form> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-if="authenticated === null" class="text-gray-300 dark:text-red-300 pt-24 pb-12"> |
|
|
|
<div |
|
|
|
v-if="authenticated === null" |
|
|
|
class="text-gray-300 dark:text-red-300 pt-24 pb-12" |
|
|
|
> |
|
|
|
<svg |
|
|
|
class="w-5 animate-spin mx-auto" |
|
|
|
xmlns="http://www.w3.org/2000/svg" |
|
|
|
viewBox="0 0 24 24" |
|
|
|
fill="currentColor" |
|
|
|
> |
|
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> |
|
|
|
<circle |
|
|
|
class="opacity-25" |
|
|
|
cx="12" |
|
|
|
cy="12" |
|
|
|
r="10" |
|
|
|
stroke="currentColor" |
|
|
|
stroke-width="4" |
|
|
|
></circle> |
|
|
|
<path |
|
|
|
class="opacity-75" |
|
|
|
fill="currentColor" |
|
|
|
@ -1260,11 +1346,31 @@ export default { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<p v-cloak class="text-center m-10 text-gray-300 dark:text-neutral-600 text-xs"> |
|
|
|
<p |
|
|
|
v-cloak |
|
|
|
class="text-center m-10 text-gray-300 dark:text-neutral-600 text-xs" |
|
|
|
> |
|
|
|
Made by |
|
|
|
<a target="_blank" class="hover:underline" href="https://emilenijssen.nl/?ref=wg-easy">Emile Nijssen</a> · |
|
|
|
<a class="hover:underline" href="https://github.com/sponsors/WeeJeWel" target="_blank">Donate</a> · |
|
|
|
<a class="hover:underline" href="https://github.com/wg-easy/wg-easy" target="_blank">GitHub</a> |
|
|
|
<a |
|
|
|
target="_blank" |
|
|
|
class="hover:underline" |
|
|
|
href="https://emilenijssen.nl/?ref=wg-easy" |
|
|
|
>Emile Nijssen</a |
|
|
|
> |
|
|
|
· |
|
|
|
<a |
|
|
|
class="hover:underline" |
|
|
|
href="https://github.com/sponsors/WeeJeWel" |
|
|
|
target="_blank" |
|
|
|
>Donate</a |
|
|
|
> |
|
|
|
· |
|
|
|
<a |
|
|
|
class="hover:underline" |
|
|
|
href="https://github.com/wg-easy/wg-easy" |
|
|
|
target="_blank" |
|
|
|
>GitHub</a |
|
|
|
> |
|
|
|
</p> |
|
|
|
</main> |
|
|
|
</template> |
|
|
|
|