fix: save key and load key

This commit is contained in:
Ren Amamiya
2026-04-08 13:16:36 +07:00
parent 4050afe93f
commit 3dd032e238
6 changed files with 257 additions and 247 deletions

View File

@@ -1,144 +1,144 @@
import browser from "webextension-polyfill";
import browser from 'webextension-polyfill'
export const NO_PERMISSIONS_REQUIRED = {
replaceURL: true,
peekPublicKey: true,
};
replaceURL: true,
peekPublicKey: true
}
export const PERMISSION_NAMES = Object.fromEntries([
["getPublicKey", "read your public key"],
["signEvent", "sign events using your private key"],
["nip04.encrypt", "encrypt messages to peers"],
["nip04.decrypt", "decrypt messages from peers"],
["nip44.encrypt", "encrypt messages to peers"],
["nip44.decrypt", "decrypt messages from peers"],
]);
['getPublicKey', 'read your public key'],
['signEvent', 'sign events using your private key'],
['nip04.encrypt', 'encrypt messages to peers'],
['nip04.decrypt', 'decrypt messages from peers'],
['nip44.encrypt', 'encrypt messages to peers'],
['nip44.decrypt', 'decrypt messages from peers']
])
function matchConditions(conditions, event) {
if (conditions?.kinds) {
if (event.kind in conditions.kinds) return true;
else return false;
}
if (conditions?.kinds) {
if (event.kind in conditions.kinds) return true
else return false
}
return true;
return true
}
export async function getPermissionStatus(host, type, event) {
const { policies } = await browser.storage.local.get("policies");
const { policies } = await browser.storage.local.get('policies')
const answers = [true, false];
for (let i = 0; i < answers.length; i++) {
const accept = answers[i];
const { conditions } = policies?.[host]?.[accept]?.[type] || {};
const answers = [true, false]
for (let i = 0; i < answers.length; i++) {
const accept = answers[i]
const { conditions } = policies?.[host]?.[accept]?.[type] || {}
if (conditions) {
if (type === "signEvent") {
if (matchConditions(conditions, event)) {
return accept; // may be true or false
} else {
}
} else {
return accept; // may be true or false
}
}
}
if (conditions) {
if (type === 'signEvent') {
if (matchConditions(conditions, event)) {
return accept // may be true or false
} else {
}
} else {
return accept // may be true or false
}
}
}
return undefined;
return undefined
}
export async function updatePermission(host, type, accept, conditions) {
const { policies = {} } = await browser.storage.local.get("policies");
const { policies = {} } = await browser.storage.local.get('policies')
// if the new conditions is "match everything", override the previous
if (Object.keys(conditions).length === 0) {
conditions = {};
} else {
// if we already had a policy for this, merge the conditions
const existingConditions = policies[host]?.[accept]?.[type]?.conditions;
if (existingConditions) {
if (existingConditions.kinds && conditions.kinds) {
Object.keys(existingConditions.kinds).forEach((kind) => {
conditions.kinds[kind] = true;
});
}
}
}
// if the new conditions is "match everything", override the previous
if (Object.keys(conditions).length === 0) {
conditions = {}
} else {
// if we already had a policy for this, merge the conditions
const existingConditions = policies[host]?.[accept]?.[type]?.conditions
if (existingConditions) {
if (existingConditions.kinds && conditions.kinds) {
Object.keys(existingConditions.kinds).forEach((kind) => {
conditions.kinds[kind] = true
})
}
}
}
// if we have a reverse policy (accept / reject) that is exactly equal to this, remove it
const other = !accept;
const reverse = policies?.[host]?.[other]?.[type];
if (
reverse &&
JSON.stringify(reverse.conditions) === JSON.stringify(conditions)
) {
delete policies[host][other][type];
}
// if we have a reverse policy (accept / reject) that is exactly equal to this, remove it
const other = !accept
const reverse = policies?.[host]?.[other]?.[type]
if (
reverse &&
JSON.stringify(reverse.conditions) === JSON.stringify(conditions)
) {
delete policies[host][other][type]
}
// insert our new policy
policies[host] = policies[host] || {};
policies[host][accept] = policies[host][accept] || {};
policies[host][accept][type] = {
conditions, // filter that must match the event (in case of signEvent)
created_at: Math.round(Date.now() / 1000),
};
// insert our new policy
policies[host] = policies[host] || {}
policies[host][accept] = policies[host][accept] || {}
policies[host][accept][type] = {
conditions, // filter that must match the event (in case of signEvent)
created_at: Math.round(Date.now() / 1000)
}
browser.storage.local.set({ policies });
browser.storage.local.set({ policies })
}
export async function removePermissions(host, accept, type) {
const { policies = {} } = await browser.storage.local.get("policies");
delete policies[host]?.[accept]?.[type];
browser.storage.local.set({ policies });
const { policies = {} } = await browser.storage.local.get('policies')
delete policies[host]?.[accept]?.[type]
browser.storage.local.set({ policies })
}
export async function showNotification(host, answer, type, params) {
const { notifications } = await browser.storage.local.get("notifications");
if (notifications) {
const action = answer ? "allowed" : "denied";
browser.notifications.create(undefined, {
type: "basic",
title: `${type} ${action} for ${host}`,
message: JSON.stringify(
params?.event
? {
kind: params.event.kind,
content: params.event.content,
tags: params.event.tags,
}
: params,
null,
2,
),
iconUrl: "icons/48x48.png",
});
}
const { notifications } = await browser.storage.local.get('notifications')
if (notifications) {
const action = answer ? 'allowed' : 'denied'
browser.notifications.create(undefined, {
type: 'basic',
title: `${type} ${action} for ${host}`,
message: JSON.stringify(
params?.event
? {
kind: params.event.kind,
content: params.event.content,
tags: params.event.tags
}
: params,
null,
2
),
iconUrl: 'icons/48x48.png'
})
}
}
export async function getPosition(width, height) {
let left = 0;
let top = 0;
let left = 0
let top = 0
try {
const lastFocused = await browser.windows.getLastFocused();
try {
const lastFocused = await browser.windows.getLastFocused()
if (
lastFocused &&
lastFocused.top !== undefined &&
lastFocused.left !== undefined &&
lastFocused.width !== undefined &&
lastFocused.height !== undefined
) {
top = Math.round(lastFocused.top + (lastFocused.height - height) / 2);
left = Math.round(lastFocused.left + (lastFocused.width - width) / 2);
} else {
console.error("Last focused window properties are undefined.");
}
} catch (error) {
console.error("Error getting window position:", error);
}
if (
lastFocused &&
lastFocused.top !== undefined &&
lastFocused.left !== undefined &&
lastFocused.width !== undefined &&
lastFocused.height !== undefined
) {
top = Math.round(lastFocused.top + (lastFocused.height - height) / 2)
left = Math.round(lastFocused.left + (lastFocused.width - width) / 2)
} else {
console.error('Last focused window properties are undefined.')
}
} catch (error) {
console.error('Error getting window position:', error)
}
return {
top,
left,
};
return {
top,
left
}
}

View File

@@ -1,132 +1,130 @@
const EXTENSION = "nostrconnect";
const EXTENSION = 'nostrconnect'
window.nostr = {
_requests: {},
_pubkey: null,
_requests: {},
_pubkey: null,
async getPublicKey() {
if (this._pubkey) return this._pubkey;
this._pubkey = await this._call("getPublicKey", {});
return this._pubkey;
},
async getPublicKey() {
if (this._pubkey) return this._pubkey
this._pubkey = await this._call('getPublicKey', {})
return this._pubkey
},
async peekPublicKey() {
return this._call("peekPublicKey", {});
},
async peekPublicKey() {
return this._call('peekPublicKey', {})
},
async signEvent(event) {
return this._call("signEvent", { event });
},
async signEvent(event) {
return this._call('signEvent', { event })
},
async getRelays() {
return {};
},
async getRelays() {
return {}
},
nip04: {
async encrypt(peer, plaintext) {
return window.nostr._call("nip04.encrypt", { peer, plaintext });
},
nip04: {
async encrypt(peer, plaintext) {
return window.nostr._call('nip04.encrypt', { peer, plaintext })
},
async decrypt(peer, ciphertext) {
return window.nostr._call("nip04.decrypt", { peer, ciphertext });
},
},
async decrypt(peer, ciphertext) {
return window.nostr._call('nip04.decrypt', { peer, ciphertext })
}
},
nip44: {
async encrypt(peer, plaintext) {
return window.nostr._call("nip44.encrypt", { peer, plaintext });
},
nip44: {
async encrypt(peer, plaintext) {
return window.nostr._call('nip44.encrypt', { peer, plaintext })
},
async decrypt(peer, ciphertext) {
return window.nostr._call("nip44.decrypt", { peer, ciphertext });
},
},
async decrypt(peer, ciphertext) {
return window.nostr._call('nip44.decrypt', { peer, ciphertext })
}
},
_call(type, params) {
const id = Math.random().toString().slice(-4);
console.log(
"%c[nostrconnect:%c" +
id +
"%c]%c calling %c" +
type +
"%c with %c" +
JSON.stringify(params || {}),
"background-color:#f1b912;font-weight:bold;color:white",
"background-color:#f1b912;font-weight:bold;color:#a92727",
"background-color:#f1b912;color:white;font-weight:bold",
"color:auto",
"font-weight:bold;color:#08589d;font-family:monospace",
"color:auto",
"font-weight:bold;color:#90b12d;font-family:monospace",
);
return new Promise((resolve, reject) => {
this._requests[id] = { resolve, reject };
window.postMessage(
{
id,
ext: EXTENSION,
type,
params,
},
"*",
);
});
},
};
_call(type, params) {
const id = Math.random().toString().slice(-4)
console.log(
'%c[nostrconnect:%c' +
id +
'%c]%c calling %c' +
type +
'%c with %c' +
JSON.stringify(params || {}),
'background-color:#f1b912;font-weight:bold;color:white',
'background-color:#f1b912;font-weight:bold;color:#a92727',
'background-color:#f1b912;color:white;font-weight:bold',
'color:auto',
'font-weight:bold;color:#08589d;font-family:monospace',
'color:auto',
'font-weight:bold;color:#90b12d;font-family:monospace'
)
return new Promise((resolve, reject) => {
this._requests[id] = { resolve, reject }
window.postMessage(
{
id,
ext: EXTENSION,
type,
params
},
'*'
)
})
}
}
window.addEventListener("message", (message) => {
if (
!message.data ||
message.data.response === null ||
message.data.response === undefined ||
message.data.ext !== EXTENSION ||
!window.nostr._requests[message.data.id]
)
return;
window.addEventListener('message', (message) => {
if (
!message.data ||
message.data.response === null ||
message.data.response === undefined ||
message.data.ext !== EXTENSION ||
!window.nostr._requests[message.data.id]
)
return
if (message.data.response.error) {
const error = new Error(
`${EXTENSION}: ${message.data.response.error.message}`,
);
error.stack = message.data.response.error.stack;
window.nostr._requests[message.data.id].reject(error);
} else {
window.nostr._requests[message.data.id].resolve(message.data.response);
}
if (message.data.response.error) {
const error = new Error(
`${EXTENSION}: ${message.data.response.error.message}`
)
error.stack = message.data.response.error.stack
window.nostr._requests[message.data.id].reject(error)
} else {
window.nostr._requests[message.data.id].resolve(message.data.response)
}
console.log(
"%c[nostrconnect:%c" +
message.data.id +
"%c]%c result: %c" +
JSON.stringify(
message?.data?.response ||
message?.data?.response?.error?.message ||
{},
),
"background-color:#f1b912;font-weight:bold;color:white",
"background-color:#f1b912;font-weight:bold;color:#a92727",
"background-color:#f1b912;color:white;font-weight:bold",
"color:auto",
"font-weight:bold;color:#08589d",
);
console.log(
'%c[nostrconnect:%c' +
message.data.id +
'%c]%c result: %c' +
JSON.stringify(
message?.data?.response || message?.data?.response?.error?.message || {}
),
'background-color:#f1b912;font-weight:bold;color:white',
'background-color:#f1b912;font-weight:bold;color:#a92727',
'background-color:#f1b912;color:white;font-weight:bold',
'color:auto',
'font-weight:bold;color:#08589d'
)
delete window.nostr._requests[message.data.id];
});
delete window.nostr._requests[message.data.id]
})
// hack to replace nostr:nprofile.../etc links with something else
let replacing = null;
document.addEventListener("mousedown", replaceNostrSchemeLink);
let replacing = null
document.addEventListener('mousedown', replaceNostrSchemeLink)
async function replaceNostrSchemeLink(e) {
if (e.target.tagName !== "A" || !e.target.href.startsWith("nostr:")) return;
if (replacing === false) return;
if (e.target.tagName !== 'A' || !e.target.href.startsWith('nostr:')) return
if (replacing === false) return
const response = await window.nostr._call("replaceURL", {
url: e.target.href,
});
if (response === false) {
replacing = false;
return;
}
const response = await window.nostr._call('replaceURL', {
url: e.target.href
})
if (response === false) {
replacing = false
return
}
e.target.href = response;
e.target.href = response
}

View File

@@ -31774,7 +31774,9 @@ For more info, visit https://reactjs.org/link/mock-scheduler`);
(0, import_react3.useEffect)(() => {
import_webextension_polyfill2.default.storage.local.get(["private_key", "relays", "protocol_handler", "notifications"]).then((results) => {
if (results.private_key) {
setPrivKey(nip19_exports.nsecEncode(results.private_key));
const pkey = results.private_key;
const nsec = nip19_exports.nsecEncode(hexToBytes(pkey));
setPrivKey(nsec);
}
if (results.relays) {
const relaysList = [];
@@ -32327,8 +32329,7 @@ examples:
addUnsavedChanges("private_key");
}
async function generate() {
const sk = generateSecretKey();
setPrivKey(nip19_exports.nsecEncode(utils_exports.bytesToHex(sk)));
setPrivKey(nip19_exports.nsecEncode(generateSecretKey()));
addUnsavedChanges("private_key");
}
async function saveKey() {
@@ -32342,20 +32343,17 @@ examples:
if (type === "nsec")
hexOrEmptyKey = bytesToHex(data);
} catch (_) {
showMessage("Invalid private key format.");
return;
}
await import_webextension_polyfill2.default.storage.local.set({
private_key: hexOrEmptyKey
});
if (hexOrEmptyKey !== "") {
setPrivKey(nip19_exports.nsecEncode(hexToBytes(hexOrEmptyKey)));
}
showMessage("saved private key!");
showMessage("Private Key has been saved.");
}
function isKeyValid() {
if (privKey === "")
return true;
if (privKey.match(/^[a-f0-9]{64}$/))
return true;
try {
if (nip19_exports.decode(privKey).type === "nsec")
return true;

View File

@@ -31412,6 +31412,10 @@ For more info, visit https://reactjs.org/link/mock-scheduler`);
var Trigger = TabsTrigger;
var Content = TabsContent;
// node_modules/.pnpm/nostr-tools@2.23.3/node_modules/nostr-tools/lib/esm/utils.js
var utf8Decoder2 = new TextDecoder("utf-8");
var utf8Encoder2 = new TextEncoder();
// extension/popup.jsx
var import_jsx_runtime = __toESM(require_jsx_runtime());
function Popup() {
@@ -31428,9 +31432,9 @@ For more info, visit https://reactjs.org/link/mock-scheduler`);
(0, import_react3.useEffect)(() => {
import_webextension_polyfill.default.storage.local.get(["private_key", "relays"]).then((results) => {
if (results.private_key) {
const hexKey = getPublicKey(results.private_key);
const npubKey = nip19_exports.npubEncode(hexKey);
setKeys({ npub: npubKey, hex: hexKey });
const hexKey = getPublicKey(hexToBytes(results.private_key));
const npub = nip19_exports.npubEncode(hexKey);
setKeys({ npub, hex: hexKey });
if (results.relays) {
const relaysList = [];
for (const url in results.relays) {