Browse Source

update frontend

pull/1244/head
Bernd Storath 12 months ago
parent
commit
331b546480
  1. 4
      src/app.vue
  2. 38
      src/components/Client/ExpireDate.vue
  3. 19
      src/components/Client/OneTimeLink.vue
  4. 3
      src/components/Client/OneTimeLinkBtn.vue
  5. 12
      src/components/Clients/Sort.vue
  6. 12
      src/i18n.config.ts
  7. 8
      src/pages/login.vue
  8. 2
      src/server/utils/WireGuard.ts
  9. 4
      src/stores/auth.ts
  10. 2
      src/stores/clients.ts
  11. 29
      src/stores/global.ts
  12. 2
      src/stores/modal.ts
  13. 2
      src/utils/api.ts

4
src/app.vue

@ -11,6 +11,10 @@ const globalStore = useGlobalStore();
globalStore.fetchTrafficStats(); globalStore.fetchTrafficStats();
globalStore.fetchChartType(); globalStore.fetchChartType();
globalStore.fetchRelease(); globalStore.fetchRelease();
globalStore.fetchOneTimeLinks();
globalStore.fetchSortClients();
globalStore.fetchExpireTime();
globalStore.fetchRememberMe();
useHead({ useHead({
bodyAttrs: { bodyAttrs: {
class: 'bg-gray-50 dark:bg-neutral-800', class: 'bg-gray-50 dark:bg-neutral-800',

38
src/components/Client/ExpireDate.vue

@ -7,40 +7,37 @@
<!-- Show --> <!-- Show -->
<input <input
v-show="clientEditExpireDateId === client.id" v-show="clientEditExpireDateId === client.id"
ref="clientExpireDateInput"
v-model="clientEditExpireDate" v-model="clientEditExpireDate"
v-on:keyup.enter=" type="text"
class="rounded border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 outline-none w-70 text-black dark:text-neutral-300 dark:placeholder:text-neutral-500 text-xs p-0"
@keyup.enter="
updateClientExpireDate(client, clientEditExpireDate); updateClientExpireDate(client, clientEditExpireDate);
clientEditExpireDate = null; clientEditExpireDate = null;
clientEditExpireDateId = null; clientEditExpireDateId = null;
" "
v-on:keyup.escape=" @keyup.escape="
clientEditExpireDate = null; clientEditExpireDate = null;
clientEditExpireDateId = null; clientEditExpireDateId = null;
" "
:ref="'client-' + client.id + '-expire'"
type="text"
class="rounded border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 outline-none w-70 text-black dark:text-neutral-300 dark:placeholder:text-neutral-500 text-xs p-0"
/> />
<span <span
v-show="clientEditExpireDateId !== client.id" v-show="clientEditExpireDateId !== client.id"
class="inline-block" class="inline-block"
>{{ client.expiredAt | expiredDateFormat }}</span >{{ expiredDateFormat(client.expireAt) }}</span
> >
<!-- Edit --> <!-- Edit -->
<span <span
v-show="clientEditExpireDateId !== client.id" v-show="clientEditExpireDateId !== client.id"
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity"
@click=" @click="
clientEditExpireDate = client.expiredAt clientEditExpireDate = client.expireAt
? client.expiredAt.toISOString().slice(0, 10) ? client.expireAt.slice(0, 10)
: 'yyyy-mm-dd'; : 'yyyy-mm-dd';
clientEditExpireDateId = client.id; clientEditExpireDateId = client.id;
setTimeout( nextTick(() => clientExpireDateInput?.select());
() => $refs['client-' + client.id + '-expire'][0].select(),
1
);
" "
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -66,6 +63,11 @@ defineProps<{ client: LocalClient }>();
const globalStore = useGlobalStore(); const globalStore = useGlobalStore();
const clientsStore = useClientsStore(); const clientsStore = useClientsStore();
const clientEditExpireDate = ref<string | null>(null);
const clientEditExpireDateId = ref<string | null>(null);
const { t, locale } = useI18n();
const clientExpireDateInput = ref<HTMLInputElement | null>(null);
function updateClientExpireDate( function updateClientExpireDate(
client: LocalClient, client: LocalClient,
@ -76,4 +78,14 @@ function updateClientExpireDate(
.catch((err) => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => clientsStore.refresh().catch(console.error)); .finally(() => clientsStore.refresh().catch(console.error));
} }
function expiredDateFormat(value: string | null) {
if (value === null) return t('Permanent');
const dateTime = new Date(value);
return dateTime.toLocaleDateString(locale.value, {
year: 'numeric',
month: 'long',
day: 'numeric',
});
}
</script> </script>

19
src/components/Client/OneTimeLink.vue

@ -1,21 +1,26 @@
<template> <template>
<div <div
v-if=" v-if="
enableOneTimeLinks && globalStore.enableOneTimeLinks &&
client.oneTimeLink !== null && client.oneTimeLink !== null &&
client.oneTimeLink !== '' client.oneTimeLink !== ''
" "
:ref="'client-' + client.id + '-link'" :ref="'client-' + client.id + '-link'"
class="text-gray-400 text-xs" class="text-gray-400 text-xs"
> >
<a :href="'./cnf/' + client.oneTimeLink + ''" <a :href="'./cnf/' + client.oneTimeLink + ''">{{ path }}</a>
>{{ document.location.protocol }}//{{ document.location.host }}/cnf/{{
client.oneTimeLink
}}</a
>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ client: LocalClient }>(); const props = defineProps<{ client: LocalClient }>();
const globalStore = useGlobalStore();
const path = computed(() => {
if (import.meta.client) {
return `${document.location.protocol}//${document.location.host}/cnf/${props.client.oneTimeLink}`;
}
return '';
});
</script> </script>

3
src/components/Client/OneTimeLinkBtn.vue

@ -1,6 +1,6 @@
<template> <template>
<button <button
v-if="enableOneTimeLinks" v-if="globalStore.enableOneTimeLinks"
:disabled="!client.downloadableConfig" :disabled="!client.downloadableConfig"
class="align-middle inline-block bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition" class="align-middle inline-block bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition"
:class="{ :class="{
@ -36,6 +36,7 @@
defineProps<{ client: LocalClient }>(); defineProps<{ client: LocalClient }>();
const clientsStore = useClientsStore(); const clientsStore = useClientsStore();
const globalStore = useGlobalStore();
function showOneTimeLink(client: LocalClient) { function showOneTimeLink(client: LocalClient) {
api api

12
src/components/Clients/Sort.vue

@ -1,11 +1,11 @@
<template> <template>
<button <button
v-if="enableSortClient" v-if="globalStore.enableSortClient"
@click="sortClient = !sortClient"
class="hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-x-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 md:rounded inline-flex items-center transition" class="hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-x-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 md:rounded inline-flex items-center transition"
@click="globalStore.sortClient = !globalStore.sortClient"
> >
<svg <svg
v-if="sortClient === true" v-if="globalStore.sortClient === true"
inline inline
class="w-4 md:mr-2" class="w-4 md:mr-2"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -24,7 +24,7 @@
/> />
</svg> </svg>
<svg <svg
v-if="sortClient === false" v-if="globalStore.sortClient === false"
inline inline
class="w-4 md:mr-2" class="w-4 md:mr-2"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -46,4 +46,6 @@
</button> </button>
</template> </template>
<script setup lang="ts"></script> <script setup lang="ts">
const globalStore = useGlobalStore();
</script>

12
src/i18n.config.ts

@ -40,6 +40,12 @@ export default defineI18nConfig(() => ({
backup: 'Backup', backup: 'Backup',
titleRestoreConfig: 'Restore your configuration', titleRestoreConfig: 'Restore your configuration',
titleBackupConfig: 'Backup your configuration', titleBackupConfig: 'Backup your configuration',
rememberMe: 'Remember me',
titleRememberMe: 'Stay logged after closing the browser',
sort: 'Sort',
ExpireDate: 'Expire Date',
Permanent: 'Permanent',
OneTimeLink: 'Generate short one time link',
}, },
ua: { ua: {
name: 'Ім`я', name: 'Ім`я',
@ -118,6 +124,12 @@ export default defineI18nConfig(() => ({
backup: 'Резервная копия', backup: 'Резервная копия',
titleRestoreConfig: 'Восстановить конфигурацию', titleRestoreConfig: 'Восстановить конфигурацию',
titleBackupConfig: 'Создать резервную копию конфигурации', titleBackupConfig: 'Создать резервную копию конфигурации',
rememberMe: 'Запомнить меня',
titleRememberMe: 'Оставаться в системе после закрытия браузера',
sort: 'Сортировка',
ExpireDate: 'Дата истечения срока',
Permanent: 'Бессрочно',
OneTimeLink: 'Создать короткую одноразовую ссылку',
}, },
tr: { tr: {
// Müslüm Barış Korkmazer @babico // Müslüm Barış Korkmazer @babico

8
src/pages/login.vue

@ -28,11 +28,11 @@
/> />
<label <label
v-if="rememberMeEnabled" v-if="globalStore.rememberMeEnabled"
class="inline-block mb-5 cursor-pointer whitespace-nowrap" class="inline-block mb-5 cursor-pointer whitespace-nowrap"
:title="$t('titleRememberMe')" :title="$t('titleRememberMe')"
> >
<input type="checkbox" class="sr-only" v-model="remember" /> <input v-model="remember" type="checkbox" class="sr-only" />
<div <div
v-if="remember" v-if="remember"
@ -76,8 +76,10 @@
<script setup lang="ts"> <script setup lang="ts">
const authenticating = ref(false); const authenticating = ref(false);
const remember = ref(false);
const password = ref<null | string>(null); const password = ref<null | string>(null);
const authStore = useAuthStore(); const authStore = useAuthStore();
const globalStore = useGlobalStore();
async function login(e: Event) { async function login(e: Event) {
e.preventDefault(); e.preventDefault();
@ -87,7 +89,7 @@ async function login(e: Event) {
authenticating.value = true; authenticating.value = true;
try { try {
const res = await authStore.login(password.value); const res = await authStore.login(password.value, remember.value);
if (res) { if (res) {
await navigateTo('/'); await navigateTo('/');
} }

2
src/server/utils/WireGuard.ts

@ -166,7 +166,7 @@ ${
publicKey: client.publicKey, publicKey: client.publicKey,
createdAt: new Date(client.createdAt), createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt), updatedAt: new Date(client.updatedAt),
expiredAt: client.expireAt !== null ? new Date(client.expireAt) : null, expireAt: client.expireAt !== null ? new Date(client.expireAt) : null,
allowedIPs: client.allowedIPs, allowedIPs: client.allowedIPs,
oneTimeLink: client.oneTimeLink ?? null, oneTimeLink: client.oneTimeLink ?? null,
oneTimeLinkExpiresAt: client.oneTimeLinkExpiresAt ?? null, oneTimeLinkExpiresAt: client.oneTimeLinkExpiresAt ?? null,

4
src/stores/auth.ts

@ -4,8 +4,8 @@ export const useAuthStore = defineStore('Auth', () => {
/** /**
* @throws if unsuccessful * @throws if unsuccessful
*/ */
async function login(password: string) { async function login(password: string, remember: boolean) {
const response = await api.createSession({ password }); const response = await api.createSession({ password, remember });
requiresPassword.value = response.requiresPassword; requiresPassword.value = response.requiresPassword;
return true as const; return true as const;
} }

2
src/stores/clients.ts

@ -108,7 +108,7 @@ export const useClientsStore = defineStore('Clients', () => {
}; };
}); });
if (globalStore.enableSortClient) { if (globalStore.enableSortClient && transformedClients !== undefined) {
transformedClients = sortByProperty( transformedClients = sortByProperty(
transformedClients, transformedClients,
'name', 'name',

29
src/stores/global.ts

@ -9,7 +9,8 @@ export const useGlobalStore = defineStore('Global', () => {
); );
const uiTrafficStats = ref(false); const uiTrafficStats = ref(false);
const rememberMeEnabled = ref(false); const rememberMeEnabled = ref(false);
const enableExpireTime = ref(false);
const enableOneTimeLinks = ref(false);
const enableSortClient = ref(false); const enableSortClient = ref(false);
const sortClient = ref(true); // Sort clients by name, true = asc, false = desc const sortClient = ref(true); // Sort clients by name, true = asc, false = desc
@ -46,6 +47,26 @@ export const useGlobalStore = defineStore('Global', () => {
uiTrafficStats.value = trafficStats.value ?? false; uiTrafficStats.value = trafficStats.value ?? false;
} }
async function fetchOneTimeLinks() {
const { data: oneTimeLinks } = await api.getEnableOneTimeLinks();
enableOneTimeLinks.value = oneTimeLinks.value ?? false;
}
async function fetchSortClients() {
const { data: sortClients } = await api.getSortClients();
enableSortClient.value = sortClients.value ?? false;
}
async function fetchExpireTime() {
const { data: expireTime } = await api.getEnableExpireTime();
enableExpireTime.value = expireTime.value ?? false;
}
async function fetchRememberMe() {
const { data: rememberMe } = await api.getRememberMeEnabled();
rememberMeEnabled.value = rememberMe.value ?? false;
}
const updateCharts = computed(() => { const updateCharts = computed(() => {
return uiChartType.value > 0 && uiShowCharts.value; return uiChartType.value > 0 && uiShowCharts.value;
}); });
@ -58,8 +79,14 @@ export const useGlobalStore = defineStore('Global', () => {
rememberMeEnabled, rememberMeEnabled,
enableSortClient, enableSortClient,
sortClient, sortClient,
enableExpireTime,
enableOneTimeLinks,
fetchRelease, fetchRelease,
fetchChartType, fetchChartType,
fetchTrafficStats, fetchTrafficStats,
fetchOneTimeLinks,
fetchSortClients,
fetchExpireTime,
fetchRememberMe,
}; };
}); });

2
src/stores/modal.ts

@ -5,7 +5,7 @@ export const useModalStore = defineStore('Modal', () => {
const clientDelete = ref<null | WGClient>(null); const clientDelete = ref<null | WGClient>(null);
const clientCreate = ref<null | boolean>(null); const clientCreate = ref<null | boolean>(null);
const clientCreateName = ref<string>(''); const clientCreateName = ref<string>('');
const clientExpireDate = ref(null); const clientExpireDate = ref<string | null>(null);
const qrcode = ref<null | string>(null); const qrcode = ref<null | string>(null);
function createClient() { function createClient() {

2
src/utils/api.ts

@ -78,7 +78,7 @@ class API {
expireDate, expireDate,
}: { }: {
name: string; name: string;
expireDate: string; expireDate: string | null;
}) { }) {
return $fetch('/api/wireguard/client', { return $fetch('/api/wireguard/client', {
method: 'post', method: 'post',

Loading…
Cancel
Save