aboutsummaryrefslogtreecommitdiff
path: root/extension/src/features
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-11-22 02:21:53 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2023-11-22 02:21:53 -0500
commit2ba94602a87c87b47f566745bdab40ce75e0e879 (patch)
tree396cb9c6d73d6bfb4e4b2d4fb440d7656fe493e0 /extension/src/features
parent43429314c0989b423e116be3e9f222eba5b636c3 (diff)
latest patches, remove webext-bridge, lastest vnlib.browser
Diffstat (limited to 'extension/src/features')
-rw-r--r--extension/src/features/framework/index.ts44
-rw-r--r--extension/src/features/identity-api.ts59
-rw-r--r--extension/src/features/nip07allow-api.ts16
-rw-r--r--extension/src/features/nostr-api.ts40
-rw-r--r--extension/src/features/server-api/index.ts28
-rw-r--r--extension/src/features/settings.ts4
-rw-r--r--extension/src/features/types.ts14
7 files changed, 125 insertions, 80 deletions
diff --git a/extension/src/features/framework/index.ts b/extension/src/features/framework/index.ts
index c58ca68..44ae031 100644
--- a/extension/src/features/framework/index.ts
+++ b/extension/src/features/framework/index.ts
@@ -15,12 +15,11 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import { runtime } from "webextension-polyfill";
-import { createBackgroundPort } from '../../webext-bridge'
-import { BridgeMessage, RuntimeContext, isInternalEndpoint } from "../../webext-bridge";
import { serializeError, deserializeError } from 'serialize-error';
import { JsonObject } from "type-fest";
-import { cloneDeep, isObjectLike, set } from "lodash";
+import { cloneDeep, isArray, isObjectLike, set } from "lodash";
import { debugLog } from "@vnuge/vnlib.browser";
+import { ChannelContext, createMessageChannel } from "../../messaging";
export interface BgRuntime<T> {
readonly state: T;
@@ -68,16 +67,15 @@ export interface IBackgroundWrapper<TState> {
}
export interface ProtectedFunction extends Function {
- readonly protection: RuntimeContext[]
+ readonly protection: ChannelContext[]
}
export const optionsOnly = <T extends Function>(func: T): T => protectMethod(func, 'options');
export const popupOnly = <T extends Function>(func: T): T => protectMethod(func, 'popup');
export const contentScriptOnly = <T extends Function>(func: T): T => protectMethod(func, 'content-script');
-export const windowOnly = <T extends Function>(func: T): T => protectMethod(func, 'window');
export const popupAndOptionsOnly = <T extends Function>(func: T): T => protectMethod(func, 'popup', 'options');
-export const protectMethod = <T extends Function>(func: T, ...protection: RuntimeContext[]): T => {
+export const protectMethod = <T extends Function>(func: T, ...protection: ChannelContext[]): T => {
(func as any).protection = protection
return func;
}
@@ -88,14 +86,16 @@ export const protectMethod = <T extends Function>(func: T, ...protection: Runtim
*/
export const useBackgroundFeatures = <TState>(state: TState): IBackgroundWrapper<TState> => {
+ const { openOnMessageChannel } = createMessageChannel('background');
+ const { onMessage } = openOnMessageChannel()
+
+
const rt = {
state,
onConnected: runtime.onConnect.addListener,
onInstalled: runtime.onInstalled.addListener,
} as BgRuntime<TState>
- const { onMessage } = createBackgroundPort()
-
/**
* Each plugin will export named methods. Background methods
* are captured and registered as on-message handlers that
@@ -120,20 +120,25 @@ export const useBackgroundFeatures = <TState>(state: TState): IBackgroundWrapper
const onMessageFuncName = `${feature.name}-${externFuncName}`
//register method with api
- onMessage(onMessageFuncName, async (msg: BridgeMessage<any>) => {
+ onMessage<any>(onMessageFuncName, async (sender, payload) => {
try {
- //Always an internal endpoint
- if (!isInternalEndpoint(msg.sender)) {
+ if ((func as ProtectedFunction).protection
+ && !(func as ProtectedFunction).protection.includes(sender)) {
throw new Error(`Unauthorized external call to ${onMessageFuncName}`)
}
- if ((func as ProtectedFunction).protection
- && !(func as ProtectedFunction).protection.includes(msg.sender.context)) {
- throw new Error(`Unauthorized external call to ${onMessageFuncName}`)
+ const res = await func(...payload)
+
+ if(isArray(res)){
+ return [...res]
+ }
+ else if(isObjectLike(res)){
+ return { ...res }
+ }
+ else{
+ return res
}
- const res = await func(...msg.data)
- return isObjectLike(res) ? { ...res} : res
}
catch (e: any) {
debugLog(`Error in method ${onMessageFuncName}`, e)
@@ -154,8 +159,11 @@ export const useBackgroundFeatures = <TState>(state: TState): IBackgroundWrapper
* Creates a foreground runtime context for unwrapping foreground stub
* methods and redirecting them to thier background handler
*/
-export const useForegoundFeatures = (sendMessage: SendMessageHandler): IForegroundUnwrapper => {
+export const useForegoundFeatures = (context: ChannelContext): IForegroundUnwrapper => {
+ const { openChannel } = createMessageChannel(context);
+ const { sendMessage } = openChannel()
+
/**
* The goal of this function is to get the foreground interface object
* that should match the background implementation. All methods are
@@ -211,4 +219,4 @@ export const exportForegroundApi = <T extends FeatureApi>(args: DummyApiExport<T
}
return () => type
-} \ No newline at end of file
+}
diff --git a/extension/src/features/identity-api.ts b/extension/src/features/identity-api.ts
index 07b6387..d7db5ff 100644
--- a/extension/src/features/identity-api.ts
+++ b/extension/src/features/identity-api.ts
@@ -24,10 +24,10 @@ import {
exportForegroundApi
} from "./framework";
import { AppSettings } from "./settings";
-import { ref, watch } from "vue";
+import { shallowRef, watch } from "vue";
import { useSession } from "@vnuge/vnlib.browser";
-import { get, set, useToggle, watchOnce } from "@vueuse/core";
-import { find, isArray } from "lodash";
+import { set, useToggle, watchOnce } from "@vueuse/core";
+import { defer, isArray } from "lodash";
export interface IdentityApi extends FeatureApi, Watchable {
createIdentity: (identity: NostrPubKey) => Promise<NostrPubKey>
@@ -45,56 +45,57 @@ export const useIdentityApi = (): IFeatureExport<AppSettings, IdentityApi> => {
const { loggedIn } = useSession();
//Get the current selected key
- const selectedKey = ref<NostrPubKey | undefined>();
+ const selectedKey = shallowRef<NostrPubKey | undefined>();
+ const allKeys = shallowRef<NostrPubKey[]>([]);
const [ onTriggered , triggerChange ] = useToggle()
+ const keyLoadWatchLoop = async () => {
+ while(true){
+ //Load keys from server if logged in
+ if(loggedIn.value){
+ const [...keys] = await execRequest(Endpoints.GetKeys);
+ allKeys.value = isArray(keys) ? keys : [];
+ }
+ else{
+ //Clear all keys when logged out
+ allKeys.value = [];
+ }
+
+ //Wait for changes to trigger a new key-load
+ await new Promise((resolve) => watchOnce([onTriggered, loggedIn] as any, () => resolve(null)))
+ }
+ }
+
+ defer(keyLoadWatchLoop)
+
//Clear the selected key if the user logs out
watch(loggedIn, (li) => li ? null : selectedKey.value = undefined)
return {
//Identity is only available in options context
createIdentity: optionsOnly(async (id: NostrPubKey) => {
- await execRequest<NostrPubKey>(Endpoints.CreateId, id)
+ await execRequest(Endpoints.CreateId, id)
triggerChange()
}),
updateIdentity: optionsOnly(async (id: NostrPubKey) => {
- await execRequest<NostrPubKey>(Endpoints.UpdateId, id)
+ await execRequest(Endpoints.UpdateId, id)
triggerChange()
}),
deleteIdentity: optionsOnly(async (key: NostrPubKey) => {
- await execRequest<NostrPubKey>(Endpoints.DeleteKey, key);
+ await execRequest(Endpoints.DeleteKey, key);
triggerChange()
}),
selectKey: popupAndOptionsOnly((key: NostrPubKey): Promise<void> => {
- selectedKey.value = key;
+ set(selectedKey, key);
return Promise.resolve()
}),
- getAllKeys: async (): Promise<NostrPubKey[]> => {
- if(!get(loggedIn)){
- return []
- }
- //Get the keys from the server
- const data = await execRequest<NostrPubKey[]>(Endpoints.GetKeys);
-
- //Response must be an array of key objects
- if (!isArray(data)) {
- return [];
- }
-
- //Make sure the selected keyid is in the list, otherwise unselect the key
- if (data?.length > 0) {
- if (!find(data, k => k.Id === selectedKey.value?.Id)) {
- set(selectedKey, undefined);
- }
- }
-
- return [...data]
+ getAllKeys: (): Promise<NostrPubKey[]> => {
+ return Promise.resolve(allKeys.value);
},
getPublicKey: (): Promise<NostrPubKey | undefined> => {
return Promise.resolve(selectedKey.value);
},
waitForChange: () => {
- console.log('Waiting for change')
return new Promise((resolve) => watchOnce([selectedKey, loggedIn, onTriggered] as any, () => resolve()))
}
}
diff --git a/extension/src/features/nip07allow-api.ts b/extension/src/features/nip07allow-api.ts
index eff4ff8..0612b66 100644
--- a/extension/src/features/nip07allow-api.ts
+++ b/extension/src/features/nip07allow-api.ts
@@ -19,7 +19,7 @@ import { defaultTo, filter, includes, isEqual } from "lodash";
import { BgRuntime, FeatureApi, IFeatureExport, exportForegroundApi, popupAndOptionsOnly } from "./framework";
import { AppSettings } from "./settings";
import { set, get, watchOnce, useToggle } from "@vueuse/core";
-import { computed, ref } from "vue";
+import { computed, shallowRef } from "vue";
interface AllowedSites{
origins: string[];
@@ -46,13 +46,13 @@ export const useInjectAllowList = (): IFeatureExport<AppSettings, InjectAllowlis
const store = useSingleSlotStorage<AllowedSites>(storage.local, 'nip07-allowlist', { origins: [], enabled: true });
//watch current tab
- const allowedOrigins = ref<string[]>([])
- const protectionEnabled = ref<boolean>(true)
+ const allowedOrigins = shallowRef<string[]>([])
+ const protectionEnabled = shallowRef<boolean>(true)
const [manullyTriggered, trigger] = useToggle()
const { currentOrigin, currentTab } = (() => {
- const currentTab = ref<Tabs.Tab | undefined>(undefined)
+ const currentTab = shallowRef<Tabs.Tab | undefined>(undefined)
const currentOrigin = computed(() => currentTab.value?.url ? new URL(currentTab.value.url).origin : undefined)
//Watch for changes to the current tab
@@ -73,7 +73,7 @@ export const useInjectAllowList = (): IFeatureExport<AppSettings, InjectAllowlis
})()
const writeChanges = async () => {
- await store.set({ origins: [...get(allowedOrigins)], enabled: get(protectionEnabled) })
+ await store.set({ origins: get(allowedOrigins), enabled: get(protectionEnabled) })
}
//Initial load
@@ -158,15 +158,15 @@ export const useInjectAllowList = (): IFeatureExport<AppSettings, InjectAllowlis
}),
async getStatus(): Promise<AllowedOriginStatus> {
return{
- allowedOrigins: [...get(allowedOrigins)],
+ allowedOrigins: get(allowedOrigins),
enabled: get(protectionEnabled),
currentOrigin: get(currentOrigin),
isAllowed: isOriginAllowed()
}
},
- waitForChange: () => {
+ async waitForChange() {
//Wait for the trigger to change
- return new Promise((resolve) => watchOnce([currentTab, protectionEnabled, manullyTriggered] as any, () => resolve()));
+ await new Promise((resolve) => watchOnce([currentTab, protectionEnabled, manullyTriggered] as any, () => resolve(null)));
},
}
},
diff --git a/extension/src/features/nostr-api.ts b/extension/src/features/nostr-api.ts
index 307522d..743f8f1 100644
--- a/extension/src/features/nostr-api.ts
+++ b/extension/src/features/nostr-api.ts
@@ -14,10 +14,11 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import { Endpoints, useServerApi } from "./server-api";
-import { NostrRelay, EventMessage, NostrEvent } from './types'
-import { FeatureApi, BgRuntime, IFeatureExport, optionsOnly, exportForegroundApi } from "./framework";
-import { AppSettings } from "./settings";
+import { type FeatureApi, type BgRuntime, type IFeatureExport, optionsOnly, exportForegroundApi } from "./framework";
+import { type AppSettings } from "./settings";
import { useTagFilter } from "./tagfilter-api";
+import type { NostrRelay, EncryptionRequest, NostrEvent } from './types';
+import { cloneDeep } from "lodash";
/**
@@ -28,8 +29,8 @@ export interface NostrApi extends FeatureApi {
getRelays: () => Promise<NostrRelay[]>;
signEvent: (event: NostrEvent) => Promise<NostrEvent | undefined>;
setRelay: (relay: NostrRelay) => Promise<NostrRelay | undefined>;
- nip04Encrypt: (data: EventMessage) => Promise<string>;
- nip04Decrypt: (data: EventMessage) => Promise<string>;
+ nip04Encrypt: (data: EncryptionRequest) => Promise<string>;
+ nip04Decrypt: (data: EncryptionRequest) => Promise<string>;
}
export const useNostrApi = (): IFeatureExport<AppSettings, NostrApi> => {
@@ -43,28 +44,41 @@ export const useNostrApi = (): IFeatureExport<AppSettings, NostrApi> => {
return {
getRelays: async (): Promise<NostrRelay[]> => {
//Get preferred relays for the current user
- const data = await execRequest<NostrRelay[]>(Endpoints.GetRelays)
- return [...data]
+ const [...relays] = await execRequest(Endpoints.GetRelays)
+ return relays;
},
signEvent: async (req: NostrEvent): Promise<NostrEvent | undefined> => {
+ //Store copy to prevent mutation
+ req = cloneDeep(req)
+
//If tag filter is enabled, filter before continuing
if(state.currentConfig.value.tagFilter){
await filterTags(req)
}
//Sign the event
- const event = await execRequest<NostrEvent>(Endpoints.SignEvent, req);
+ const event = await execRequest(Endpoints.SignEvent, req);
return event;
},
- nip04Encrypt: async (data: EventMessage): Promise<string> => {
- return execRequest<string>(Endpoints.Encrypt, data);
+ nip04Encrypt: async (data: EncryptionRequest): Promise<string> => {
+ const message: EncryptionRequest = {
+ content: data.content,
+ KeyId: data.KeyId,
+ pubkey: data.pubkey
+ }
+ return execRequest(Endpoints.Encrypt, message);
},
- nip04Decrypt: (data: EventMessage): Promise<string> => {
- return execRequest<string>(Endpoints.Decrypt, data);
+ nip04Decrypt: (data: EncryptionRequest): Promise<string> => {
+ const message: EncryptionRequest = {
+ content: data.content,
+ KeyId: data.KeyId,
+ pubkey: data.pubkey
+ }
+ return execRequest(Endpoints.Decrypt, message);
},
setRelay: optionsOnly((relay: NostrRelay): Promise<NostrRelay | undefined> => {
- return execRequest<NostrRelay>(Endpoints.SetRelay, relay)
+ return execRequest(Endpoints.SetRelay, relay)
}),
}
},
diff --git a/extension/src/features/server-api/index.ts b/extension/src/features/server-api/index.ts
index 6aa34da..6637aaa 100644
--- a/extension/src/features/server-api/index.ts
+++ b/extension/src/features/server-api/index.ts
@@ -18,10 +18,9 @@ import { computed } from "vue"
import { get } from '@vueuse/core'
import { type WebMessage, type UserProfile } from "@vnuge/vnlib.browser"
import { initEndponts } from "./endpoints"
-import { type NostrIdentiy } from "../foreground/types"
import { cloneDeep } from "lodash"
import { type AppSettings } from "../settings"
-import type { NostrEvent, NostrRelay } from "../types"
+import type { EncryptionRequest, NostrEvent, NostrPubKey, NostrRelay } from "../types"
export enum Endpoints {
GetKeys = 'getKeys',
@@ -36,7 +35,20 @@ export enum Endpoints {
UpdateProfile = 'updateProfile',
}
-export const useServerApi = (settings: AppSettings) => {
+export interface ExecRequestHandler{
+ (id: Endpoints.GetKeys):Promise<NostrPubKey[]>
+ (id: Endpoints.DeleteKey, key: NostrPubKey):Promise<void>
+ (id: Endpoints.SignEvent, event: NostrEvent):Promise<NostrEvent>
+ (id: Endpoints.GetRelays):Promise<NostrRelay[]>
+ (id: Endpoints.SetRelay, relay: NostrRelay):Promise<NostrRelay>
+ (id: Endpoints.Encrypt, data: EncryptionRequest):Promise<string>
+ (id: Endpoints.Decrypt, data: EncryptionRequest):Promise<string>
+ (id: Endpoints.CreateId, identity: NostrPubKey):Promise<NostrPubKey>
+ (id: Endpoints.UpdateId, identity: NostrPubKey):Promise<NostrPubKey>
+ (id: Endpoints.UpdateProfile, profile: UserProfile):Promise<string>
+}
+
+export const useServerApi = (settings: AppSettings): { execRequest: ExecRequestHandler } => {
const { registerEndpoint, execRequest } = initEndponts()
//ref to nostr endpoint url
@@ -54,7 +66,7 @@ export const useServerApi = (settings: AppSettings) => {
registerEndpoint({
id: Endpoints.DeleteKey,
method: 'DELETE',
- path: (key:NostrIdentiy) => `${get(nostrUrl)}?type=identity&key_id=${key.Id}`,
+ path: (key: NostrPubKey) => `${get(nostrUrl)}?type=identity&key_id=${key.Id}`,
onRequest: () => Promise.resolve(),
onResponse: async (response: WebMessage) => response.getResultOrThrow()
})
@@ -91,7 +103,7 @@ export const useServerApi = (settings: AppSettings) => {
id: Endpoints.CreateId,
method: 'PUT',
path: () => `${get(nostrUrl)}?type=identity`,
- onRequest: (identity:NostrIdentiy) => Promise.resolve(identity),
+ onRequest: (identity: NostrPubKey) => Promise.resolve(identity),
onResponse: async (response: WebMessage<NostrEvent>) => response.getResultOrThrow()
})
@@ -99,7 +111,7 @@ export const useServerApi = (settings: AppSettings) => {
id: Endpoints.UpdateId,
method: 'PATCH',
path: () => `${get(nostrUrl)}?type=identity`,
- onRequest: (identity:NostrIdentiy) => {
+ onRequest: (identity:NostrPubKey) => {
const id = cloneDeep(identity) as any;
delete id.Created;
delete id.LastModified;
@@ -121,7 +133,7 @@ export const useServerApi = (settings: AppSettings) => {
id:Endpoints.Encrypt,
method:'POST',
path: () => `${get(nostrUrl)}?type=encrypt`,
- onRequest: (data: NostrEvent) => Promise.resolve(data),
+ onRequest: (data: EncryptionRequest) => Promise.resolve(data),
onResponse: async (response: WebMessage<string>) => response.getResultOrThrow()
})
@@ -129,7 +141,7 @@ export const useServerApi = (settings: AppSettings) => {
id:Endpoints.Decrypt,
method:'POST',
path: () => `${get(nostrUrl)}?type=decrypt`,
- onRequest: (data: NostrEvent) => Promise.resolve(data),
+ onRequest: (data: EncryptionRequest) => Promise.resolve(data),
onResponse: async (response: WebMessage<string>) => response.getResultOrThrow()
})
diff --git a/extension/src/features/settings.ts b/extension/src/features/settings.ts
index a67957c..059c2d2 100644
--- a/extension/src/features/settings.ts
+++ b/extension/src/features/settings.ts
@@ -19,7 +19,7 @@ import { configureApi, debugLog } from '@vnuge/vnlib.browser'
import { readonly, ref, Ref } from "vue";
import { JsonObject } from "type-fest";
import { Watchable, useSingleSlotStorage } from "./types";
-import { BgRuntime, FeatureApi, optionsOnly, IFeatureExport, exportForegroundApi } from './framework'
+import { BgRuntime, FeatureApi, optionsOnly, IFeatureExport, exportForegroundApi, popupAndOptionsOnly } from './framework'
import { get, watchOnce } from "@vueuse/core";
export interface PluginConfig extends JsonObject {
@@ -143,7 +143,7 @@ export const useSettingsApi = () : IFeatureExport<AppSettings, SettingsApi> =>{
return state.currentConfig.value
}),
- setDarkMode: optionsOnly(async (darkMode: boolean) => {
+ setDarkMode: popupAndOptionsOnly(async (darkMode: boolean) => {
console.log('Setting dark mode to', darkMode, 'from', _darkMode.value)
_darkMode.value = darkMode
}),
diff --git a/extension/src/features/types.ts b/extension/src/features/types.ts
index bd0afee..a64be93 100644
--- a/extension/src/features/types.ts
+++ b/extension/src/features/types.ts
@@ -35,8 +35,18 @@ export interface TaggedNostrEvent extends NostrEvent {
tags?: any[][]
}
-export interface EventMessage extends JsonObject {
- readonly event: NostrEvent
+export interface EncryptionRequest extends JsonObject {
+ readonly KeyId: string
+ /**
+ * The plaintext to encrypt or ciphertext
+ * to decrypt
+ */
+ readonly content: string
+ /**
+ * The other peer's public key used
+ * for encryption
+ */
+ readonly pubkey: string
}
export interface NostrRelay extends JsonObject {