aboutsummaryrefslogtreecommitdiff
path: root/front-end/src/store
diff options
context:
space:
mode:
Diffstat (limited to 'front-end/src/store')
-rw-r--r--front-end/src/store/bookmarks.ts4
-rw-r--r--front-end/src/store/socialMfaPlugin.ts74
-rw-r--r--front-end/src/store/websiteLookup.ts76
3 files changed, 110 insertions, 44 deletions
diff --git a/front-end/src/store/bookmarks.ts b/front-end/src/store/bookmarks.ts
index 2af8344..2d12a9a 100644
--- a/front-end/src/store/bookmarks.ts
+++ b/front-end/src/store/bookmarks.ts
@@ -18,7 +18,7 @@ import { MaybeRef, shallowRef, watch, computed, Ref, ref } from 'vue';
import { apiCall, useAxios, WebMessage } from '@vnuge/vnlib.browser';
import { useToggle, get, set, useOffsetPagination, watchDebounced, syncRef } from '@vueuse/core';
import { PiniaPluginContext, PiniaPlugin, storeToRefs } from 'pinia'
-import { isArray, join, map, split, sortBy } from 'lodash-es';
+import { isArray, join, map, split, sortBy, filter, isEmpty } from 'lodash-es';
import { useQuery } from './index';
export interface Bookmark{
@@ -189,7 +189,7 @@ const searchQuery = (search: Ref<string | null>, tags: Ref<string[]>) => {
const tagQuery = useQuery('t')
const currentTags = computed({
- get: () => split(tagQuery.value, ' '),
+ get: () => filter(split(tagQuery.value, ' '), p => !isEmpty(p)),
set: (value) => set(tagQuery, join(value, ' '))
})
diff --git a/front-end/src/store/socialMfaPlugin.ts b/front-end/src/store/socialMfaPlugin.ts
index d8d7bb1..e0ec972 100644
--- a/front-end/src/store/socialMfaPlugin.ts
+++ b/front-end/src/store/socialMfaPlugin.ts
@@ -1,26 +1,19 @@
-// Copyright (C) 2024 Vaughn Nugent
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'pinia'
import { MaybeRef } from 'vue';
-import { useSocialOauthLogin, useUser, SocialOAuthPortal, fromPortals, useAxios } from '@vnuge/vnlib.browser'
+import {
+ useUser,
+ useOauthLogin,
+ useSocialDefaultLogout,
+ fetchSocialPortals,
+ fromSocialPortals,
+ fromSocialConnections,
+} from '@vnuge/vnlib.browser'
import { get } from '@vueuse/core';
import { PiniaPluginContext, PiniaPlugin, storeToRefs } from 'pinia'
import { defer } from 'lodash-es';
-type SocialMfaPlugin = ReturnType<typeof useSocialOauthLogin>
+type SocialMfaPlugin = ReturnType<typeof useOauthLogin>
declare module 'pinia' {
export interface PiniaCustomProperties {
@@ -35,43 +28,40 @@ export const socialMfaPlugin = (portalEndpoint?: MaybeRef<string>): PiniaPlugin
const { } = storeToRefs(store)
const { logout } = useUser()
- /**
- * Override the logout function to default to a social logout,
- * if the social logout fails, then we will logout the user
- */
- const setLogoutMethod = (socialOauth: SocialMfaPlugin) => {
- const logoutFunc = socialOauth.logout;
+ //Create social login from available portals
+ const defaultSocial = useSocialDefaultLogout(
+ useOauthLogin([]),
+ logout //fallback to default logout
+ );
- (socialOauth as any).logout = async () => {
- if (await logoutFunc() === false) {
- await logout()
- }
- }
- }
-
- const _loadPromise = new Promise<SocialMfaPlugin>((resolve, reject) => {
+ const _loadPromise = new Promise<SocialMfaPlugin>((resolve, _) => {
- if(get(portalEndpoint) == null) {
- const socialOauth = useSocialOauthLogin([])
- setLogoutMethod(socialOauth)
- return resolve(socialOauth)
+ if (get(portalEndpoint) == null) {
+ return resolve(defaultSocial)
}
+ /*
+ Try to load social methods from server, if it fails, then we will
+ fall back to default
+ */
+
defer(async () => {
+
try {
- //Get axios instance
- const axios = useAxios(null)
- //Get all enabled portals
- const { data } = await axios.get<SocialOAuthPortal[]>(get(portalEndpoint)!);
- //Setup social providers from server portals
- const socialOauth = useSocialOauthLogin(fromPortals(data));
- setLogoutMethod(socialOauth);
+ const portals = await fetchSocialPortals(get(portalEndpoint)!);
+ const social = fromSocialPortals(portals);
+ const methods = fromSocialConnections(social);
+
+ //Create social login from available portals
+ const login = useOauthLogin(methods);
+ const socialOauth = useSocialDefaultLogout(login, logout);
resolve(socialOauth)
} catch (error) {
- reject(error)
+ //Let failure fall back to default
+ resolve(defaultSocial)
}
})
})
diff --git a/front-end/src/store/websiteLookup.ts b/front-end/src/store/websiteLookup.ts
new file mode 100644
index 0000000..560d00f
--- /dev/null
+++ b/front-end/src/store/websiteLookup.ts
@@ -0,0 +1,76 @@
+
+import 'pinia'
+import { MaybeRef, Ref, shallowRef, watch } from 'vue';
+import { WebMessage, apiCall, useAxios } from '@vnuge/vnlib.browser'
+import { get, set } from '@vueuse/core';
+import { PiniaPluginContext, PiniaPlugin, storeToRefs } from 'pinia'
+import { defer, filter, isEmpty, noop } from 'lodash-es';
+
+export interface WebsiteLookupResult {
+ readonly title: string | undefined,
+ readonly description: string | undefined,
+ keywords: string[] | undefined,
+}
+
+export interface LookupApi{
+ isSupported: Ref<boolean>,
+ timeout: Ref<number>,
+ execLookup(url:string): Promise<WebsiteLookupResult>
+}
+
+declare module 'pinia' {
+ export interface PiniaCustomProperties {
+ websiteLookup:{
+ isSupported: boolean,
+ execLookup(url: string): Promise<WebsiteLookupResult>
+ }
+ }
+}
+
+const urlToBase64UrlEncoded = (url: string) => {
+ return btoa(url)
+ .replace(/-/g, '+')
+ .replace(/_/g, '/')
+ .replace(/\./g, '=') //Fix padding
+}
+
+export const siteLookupPlugin = (lookupEndpoint: MaybeRef<string>, to: number): PiniaPlugin => {
+
+ return ({ store }: PiniaPluginContext) => {
+
+ const { loggedIn } = storeToRefs(store)
+ const axios = useAxios(null)
+
+ const isSupported = shallowRef(false)
+ const timeout = shallowRef(to)
+
+ const checkIsSupported = () => {
+ return apiCall(async () => {
+ //Execute test with the 'support' query parameter
+ const { data } = await axios.get<WebMessage>(`${get(lookupEndpoint)}?support`)
+ set(isSupported, data.success)
+ });
+ }
+
+ const execLookup = async (url:string) => {
+ const base64Url = urlToBase64UrlEncoded(url)
+
+ //Execute test with the 'support' query parameter
+ const { data } = await axios.get<WebMessage<WebsiteLookupResult>>(`${get(lookupEndpoint)}?timeout=${get(timeout)}&url=${base64Url}`)
+ const lookup = data.getResultOrThrow();
+ lookup.keywords = filter(lookup.keywords, (k) => !isEmpty(k))
+ return lookup
+ }
+
+ //If login status changes, recheck support
+ watch([loggedIn], ([li]) => li ? defer(checkIsSupported) : noop(), { immediate: true })
+
+ return {
+ websiteLookup: {
+ isSupported,
+ execLookup,
+ timeout
+ } as LookupApi
+ } as any
+ }
+} \ No newline at end of file