add encryption/decryotion and other features
This commit is contained in:
parent
cd8085f6e5
commit
714c64387e
|
@ -9,7 +9,10 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/interfaces": "^0.0.20",
|
"@waku/interfaces": "^0.0.20",
|
||||||
|
"@waku/message-encryption": "^0.0.23",
|
||||||
"@waku/sdk": "^0.0.21",
|
"@waku/sdk": "^0.0.21",
|
||||||
|
"@waku/utils": "^0.0.13",
|
||||||
|
"ethereum-cryptography": "^2.1.2",
|
||||||
"next": "14.0.2",
|
"next": "14.0.2",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
@ -921,6 +924,61 @@
|
||||||
"integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==",
|
"integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@scure/base": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip32": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/curves": "~1.1.0",
|
||||||
|
"@noble/hashes": "~1.3.1",
|
||||||
|
"@scure/base": "~1.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip32/node_modules/@noble/curves": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.3.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip32/node_modules/@noble/hashes": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip39": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "~1.3.0",
|
||||||
|
"@scure/base": "~1.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@swc/helpers": {
|
"node_modules/@swc/helpers": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||||
|
@ -1228,6 +1286,23 @@
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@waku/message-encryption": {
|
||||||
|
"version": "0.0.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@waku/message-encryption/-/message-encryption-0.0.23.tgz",
|
||||||
|
"integrity": "sha512-i/qMAjO8EVCMlA1BbuGT+uHxzdydYQVXM9mThgH+kTTPTW5gVAMdzFaGmbiHWT6kn3yAsWq5CBB7M3+1zqb8vQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/secp256k1": "^1.7.1",
|
||||||
|
"@waku/core": "0.0.25",
|
||||||
|
"@waku/interfaces": "0.0.20",
|
||||||
|
"@waku/proto": "0.0.5",
|
||||||
|
"@waku/utils": "0.0.13",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"js-sha3": "^0.9.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@waku/peer-exchange": {
|
"node_modules/@waku/peer-exchange": {
|
||||||
"version": "0.0.18",
|
"version": "0.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/peer-exchange/-/peer-exchange-0.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/peer-exchange/-/peer-exchange-0.0.18.tgz",
|
||||||
|
@ -2650,6 +2725,39 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ethereum-cryptography": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/curves": "1.1.0",
|
||||||
|
"@noble/hashes": "1.3.1",
|
||||||
|
"@scure/bip32": "1.3.1",
|
||||||
|
"@scure/bip39": "1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ethereum-cryptography/node_modules/@noble/curves": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.3.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ethereum-cryptography/node_modules/@noble/hashes": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/event-iterator": {
|
"node_modules/event-iterator": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-2.0.0.tgz",
|
||||||
|
@ -7256,6 +7364,45 @@
|
||||||
"integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==",
|
"integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@scure/base": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q=="
|
||||||
|
},
|
||||||
|
"@scure/bip32": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||||
|
"requires": {
|
||||||
|
"@noble/curves": "~1.1.0",
|
||||||
|
"@noble/hashes": "~1.3.1",
|
||||||
|
"@scure/base": "~1.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/curves": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||||
|
"requires": {
|
||||||
|
"@noble/hashes": "1.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@noble/hashes": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@scure/bip39": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||||
|
"requires": {
|
||||||
|
"@noble/hashes": "~1.3.0",
|
||||||
|
"@scure/base": "~1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@swc/helpers": {
|
"@swc/helpers": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||||
|
@ -7488,6 +7635,20 @@
|
||||||
"resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.20.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.20.tgz",
|
||||||
"integrity": "sha512-6g2SRCKiAqtxElozXzPNHg68u/lxWSGL1LSXqwA0AAs+WYvK2vYfBM9ceUlbhDEk4ReCUAceUgZgdtdgKGflgA=="
|
"integrity": "sha512-6g2SRCKiAqtxElozXzPNHg68u/lxWSGL1LSXqwA0AAs+WYvK2vYfBM9ceUlbhDEk4ReCUAceUgZgdtdgKGflgA=="
|
||||||
},
|
},
|
||||||
|
"@waku/message-encryption": {
|
||||||
|
"version": "0.0.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@waku/message-encryption/-/message-encryption-0.0.23.tgz",
|
||||||
|
"integrity": "sha512-i/qMAjO8EVCMlA1BbuGT+uHxzdydYQVXM9mThgH+kTTPTW5gVAMdzFaGmbiHWT6kn3yAsWq5CBB7M3+1zqb8vQ==",
|
||||||
|
"requires": {
|
||||||
|
"@noble/secp256k1": "^1.7.1",
|
||||||
|
"@waku/core": "0.0.25",
|
||||||
|
"@waku/interfaces": "0.0.20",
|
||||||
|
"@waku/proto": "0.0.5",
|
||||||
|
"@waku/utils": "0.0.13",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"js-sha3": "^0.9.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@waku/peer-exchange": {
|
"@waku/peer-exchange": {
|
||||||
"version": "0.0.18",
|
"version": "0.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/@waku/peer-exchange/-/peer-exchange-0.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@waku/peer-exchange/-/peer-exchange-0.0.18.tgz",
|
||||||
|
@ -8541,6 +8702,32 @@
|
||||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ethereum-cryptography": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==",
|
||||||
|
"requires": {
|
||||||
|
"@noble/curves": "1.1.0",
|
||||||
|
"@noble/hashes": "1.3.1",
|
||||||
|
"@scure/bip32": "1.3.1",
|
||||||
|
"@scure/bip39": "1.2.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/curves": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||||
|
"requires": {
|
||||||
|
"@noble/hashes": "1.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@noble/hashes": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"event-iterator": {
|
"event-iterator": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-2.0.0.tgz",
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/interfaces": "^0.0.20",
|
"@waku/interfaces": "^0.0.20",
|
||||||
|
"@waku/message-encryption": "^0.0.23",
|
||||||
"@waku/sdk": "^0.0.21",
|
"@waku/sdk": "^0.0.21",
|
||||||
|
"@waku/utils": "^0.0.13",
|
||||||
|
"ethereum-cryptography": "^2.1.2",
|
||||||
"next": "14.0.2",
|
"next": "14.0.2",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const Loading = () => {
|
||||||
|
return (
|
||||||
|
<div className="loading-block">
|
||||||
|
<p>Loading...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -2,13 +2,17 @@
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { waku, Waku, WakuEvents } from "@/services/waku";
|
import { waku, Waku, WakuEvents } from "@/services/waku";
|
||||||
|
import { WakuStatus } from "@/const";
|
||||||
|
import { Loading } from "./Loading";
|
||||||
|
|
||||||
type WakuContextProps = {
|
type WakuContextProps = {
|
||||||
status: string;
|
status: WakuStatus;
|
||||||
|
waku?: Waku;
|
||||||
};
|
};
|
||||||
|
|
||||||
const WakuContext = React.createContext<WakuContextProps>({
|
const WakuContext = React.createContext<WakuContextProps>({
|
||||||
status: "",
|
status: WakuStatus.Initializing,
|
||||||
|
waku: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
type WakuProviderProps = {
|
type WakuProviderProps = {
|
||||||
|
@ -16,14 +20,16 @@ type WakuProviderProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useWaku = () => {
|
export const useWaku = () => {
|
||||||
const { status } = React.useContext(WakuContext);
|
const { status, waku } = React.useContext(WakuContext);
|
||||||
|
|
||||||
return { status };
|
return { status, waku };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WakuProvider = (props: WakuProviderProps) => {
|
export const WakuProvider = (props: WakuProviderProps) => {
|
||||||
const wakuRef = React.useRef<Waku>();
|
const wakuRef = React.useRef<Waku>();
|
||||||
const [status, setStatus] = React.useState<string>("");
|
const [status, setStatus] = React.useState<WakuStatus>(
|
||||||
|
WakuStatus.Initializing
|
||||||
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (wakuRef.current) {
|
if (wakuRef.current) {
|
||||||
|
@ -43,8 +49,16 @@ export const WakuProvider = (props: WakuProviderProps) => {
|
||||||
};
|
};
|
||||||
}, [wakuRef, setStatus]);
|
}, [wakuRef, setStatus]);
|
||||||
|
|
||||||
|
if (status === WakuStatus.Failed) {
|
||||||
|
return <>{status}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status !== WakuStatus.Connected) {
|
||||||
|
return <Loading />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WakuContext.Provider value={{ status }}>
|
<WakuContext.Provider value={{ status, waku: wakuRef.current }}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</WakuContext.Provider>
|
</WakuContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useWaku } from "@/app/WakuProvider";
|
|
||||||
|
|
||||||
export default function Create() {
|
|
||||||
const { status } = useWaku();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>{status}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import Markdown from "react-markdown";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { useNoteHash } from "@/hooks/useNoteHash";
|
|
||||||
|
|
||||||
const t = `
|
|
||||||
### Problem
|
|
||||||
Cannot read data returned from REST API in browser if request is done with CORS violation.
|
|
||||||
|
|
||||||
# Impact
|
|
||||||
Prevents browsers from querying REST API.
|
|
||||||
|
|
||||||
### To reproduce
|
|
||||||
0. Run nwaku REST API
|
|
||||||
1. Copy https://github.com/waku-org/waku-frontend/tree/weboko/follow-up
|
|
||||||
5. Wait for 10 seconds and see in console following error:
|
|
||||||
|
|
||||||
It happens even though the request succeds.
|
|
||||||
|
|
||||||
|
|
||||||
### Expected behavior
|
|
||||||
Error should not happen.
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
What happens is that browser implements Fetch API in the manner that when request is made to resource with CORS violation then even if it would succeed - client won't be able to read response data.
|
|
||||||
Spec of the Fetch API - https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
|
|
||||||
|
|
||||||
|
|
||||||
### The fix
|
|
||||||
Considering we expect REST API to be run only on localhost we should add following HTTP header to allow web apps run on different port to be able to talk to the API.
|
|
||||||
`;
|
|
||||||
|
|
||||||
const View = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const noteHash = useNoteHash();
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (!noteHash) {
|
|
||||||
router.replace("/404");
|
|
||||||
}
|
|
||||||
}, [noteHash]);
|
|
||||||
|
|
||||||
return <Markdown>{t}</Markdown>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default View;
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-block {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-info {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-value {
|
||||||
|
font-size: 1rem;
|
||||||
|
height: 2rem;
|
||||||
|
width: 250px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-note {
|
||||||
|
width: 100px;
|
||||||
|
height: 50px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-value {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
min-height: 500px;
|
||||||
|
}
|
|
@ -1,22 +1,79 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useWaku } from "@/app/WakuProvider";
|
import React from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { notes } from "@/services/notes";
|
||||||
|
|
||||||
export default function Home() {
|
// EgeLwHNbSwIzIz3M
|
||||||
const { status } = useWaku();
|
// 3gxFeAHa8sOvTymg
|
||||||
|
|
||||||
|
// encrypted nkt698RhpWory0yT
|
||||||
|
export default function Create() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { note, onNoteChange } = useEditNote();
|
||||||
|
const { password, onPasswordChange } = usePassword();
|
||||||
|
|
||||||
|
const onSave = async () => {
|
||||||
|
if (!note) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const id = await notes.createNote(note, password);
|
||||||
|
router.push(`/view/${id}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Failed to create a note:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>{status}</p>
|
<p className="note-info">
|
||||||
<button
|
Your record will be stored for couple of days. Markdown is supported.
|
||||||
onClick={() => {
|
</p>
|
||||||
router.push("/create");
|
<div className="create-header">
|
||||||
}}
|
<input
|
||||||
>
|
className="password-value"
|
||||||
Create new
|
type="password"
|
||||||
|
onChange={onPasswordChange}
|
||||||
|
placeholder="Optional password"
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
<button onClick={onSave} className="save-note">
|
||||||
|
Save note
|
||||||
</button>
|
</button>
|
||||||
<button>Edit existing</button>
|
</div>
|
||||||
|
<textarea
|
||||||
|
className="note-value"
|
||||||
|
value={note}
|
||||||
|
onChange={onNoteChange}
|
||||||
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useEditNote = () => {
|
||||||
|
const [state, setState] = React.useState<string>("");
|
||||||
|
|
||||||
|
const onNoteChange = (event: React.FormEvent<HTMLTextAreaElement>) => {
|
||||||
|
setState(event?.currentTarget?.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
note: state,
|
||||||
|
onNoteChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const usePassword = () => {
|
||||||
|
const [password, setPassword] = React.useState<string>();
|
||||||
|
|
||||||
|
const onPasswordChange = (event: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
setPassword(event?.currentTarget?.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
password,
|
||||||
|
onPasswordChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -4,45 +4,28 @@ import React from "react";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useNoteHash } from "@/hooks/useNoteHash";
|
import { useNoteHash } from "@/hooks/useNoteHash";
|
||||||
|
import { notes } from "@/services/notes";
|
||||||
const t = `
|
import { Loading } from "../Loading";
|
||||||
### Problem
|
|
||||||
Cannot read data returned from REST API in browser if request is done with CORS violation.
|
|
||||||
|
|
||||||
# Impact
|
|
||||||
Prevents browsers from querying REST API.
|
|
||||||
|
|
||||||
### To reproduce
|
|
||||||
0. Run nwaku REST API
|
|
||||||
1. Copy https://github.com/waku-org/waku-frontend/tree/weboko/follow-up
|
|
||||||
5. Wait for 10 seconds and see in console following error:
|
|
||||||
|
|
||||||
It happens even though the request succeds.
|
|
||||||
|
|
||||||
|
|
||||||
### Expected behavior
|
|
||||||
Error should not happen.
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
What happens is that browser implements Fetch API in the manner that when request is made to resource with CORS violation then even if it would succeed - client won't be able to read response data.
|
|
||||||
Spec of the Fetch API - https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
|
|
||||||
|
|
||||||
|
|
||||||
### The fix
|
|
||||||
Considering we expect REST API to be run only on localhost we should add following HTTP header to allow web apps run on different port to be able to talk to the API.
|
|
||||||
`;
|
|
||||||
|
|
||||||
const View = () => {
|
const View = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const noteHash = useNoteHash();
|
const noteHash = useNoteHash();
|
||||||
|
const [note, setNote] = React.useState<string>("");
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!noteHash) {
|
if (!noteHash) {
|
||||||
router.replace("/404");
|
router.replace("/404");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, [noteHash]);
|
|
||||||
|
|
||||||
return <Markdown>{t}</Markdown>;
|
notes.readNote(noteHash).then((note) => setNote(note || ""));
|
||||||
|
}, [noteHash, setNote]);
|
||||||
|
|
||||||
|
if (!note) {
|
||||||
|
return <Loading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Markdown>{note}</Markdown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default View;
|
export default View;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const CONTENT_TOPIC = "/flush-notes/1/note/proto";
|
||||||
|
|
||||||
|
export enum WakuStatus {
|
||||||
|
Initializing = "Initializing...",
|
||||||
|
WaitingForPeers = "Waiting for peers...",
|
||||||
|
Connected = "Connected",
|
||||||
|
Failed = "Failed to initialize(see logs)",
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { waku } from "@/services/waku";
|
||||||
|
import { CONTENT_TOPIC } from "@/const";
|
||||||
|
import { encrypt, decrypt } from "ethereum-cryptography/aes";
|
||||||
|
import { getRandomBytes } from "ethereum-cryptography/random";
|
||||||
|
import { pbkdf2 } from "ethereum-cryptography/pbkdf2";
|
||||||
|
import {
|
||||||
|
createDecoder,
|
||||||
|
createEncoder,
|
||||||
|
Decoder,
|
||||||
|
Encoder,
|
||||||
|
IDecodedMessage,
|
||||||
|
Unsubscribe,
|
||||||
|
utf8ToBytes,
|
||||||
|
bytesToUtf8,
|
||||||
|
} from "@waku/sdk";
|
||||||
|
import { generateRandomString } from "@/utils";
|
||||||
|
import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
|
||||||
|
|
||||||
|
type Note = {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
kdf:
|
||||||
|
| undefined
|
||||||
|
| {
|
||||||
|
iv: string;
|
||||||
|
dklen: number;
|
||||||
|
c: number;
|
||||||
|
salt: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Notes {
|
||||||
|
private decoder: Decoder;
|
||||||
|
private encoder: Encoder;
|
||||||
|
private messages: IDecodedMessage[] = [];
|
||||||
|
private subscription: undefined | Unsubscribe;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.decoder = createDecoder(CONTENT_TOPIC);
|
||||||
|
this.encoder = createEncoder({ contentTopic: CONTENT_TOPIC });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createNote(content: string, password?: string): Promise<string> {
|
||||||
|
const note = password
|
||||||
|
? await this.encryptNote(content, password)
|
||||||
|
: { id: generateRandomString(), content, kdf: undefined };
|
||||||
|
|
||||||
|
await waku.send(this.encoder, {
|
||||||
|
payload: utf8ToBytes(JSON.stringify(note)),
|
||||||
|
});
|
||||||
|
|
||||||
|
return note.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async readNote(id: string): Promise<string | undefined> {
|
||||||
|
await this.initMessages();
|
||||||
|
|
||||||
|
const message = this.messages
|
||||||
|
.map((m) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(bytesToUtf8(m.payload)) as Note;
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Failed to read message:", error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.find((v) => {
|
||||||
|
if (v?.id === id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!message?.kdf) {
|
||||||
|
return message?.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
const password = window.prompt("This note is encrypted, need password:");
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
console.log("No password was provided, stopping reading a note.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.decryptNote(message, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initMessages() {
|
||||||
|
if (this.subscription) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages = await waku.getHistory(this.decoder);
|
||||||
|
this.subscription = await waku.subscribe(this.decoder, (message) => {
|
||||||
|
this.messages.push(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async encryptNote(content: string, password: string): Promise<Note> {
|
||||||
|
const iv = await getRandomBytes(16);
|
||||||
|
const salt = await getRandomBytes(32);
|
||||||
|
const c = 131072;
|
||||||
|
const dklen = 16;
|
||||||
|
const kdf = await pbkdf2(
|
||||||
|
utf8ToBytes(password.normalize("NFKC")),
|
||||||
|
salt,
|
||||||
|
c,
|
||||||
|
dklen,
|
||||||
|
"sha256"
|
||||||
|
);
|
||||||
|
const encryptedContent = await encrypt(utf8ToBytes(content), kdf, iv);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: generateRandomString(),
|
||||||
|
content: bytesToHex(encryptedContent),
|
||||||
|
kdf: {
|
||||||
|
c,
|
||||||
|
dklen,
|
||||||
|
iv: bytesToHex(iv),
|
||||||
|
salt: bytesToHex(salt),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async decryptNote(note: Note, password: string): Promise<string> {
|
||||||
|
if (!note?.kdf) {
|
||||||
|
throw Error("Failed to decrypt a note, no kdf params found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const iv = hexToBytes(note.kdf.iv);
|
||||||
|
const salt = hexToBytes(note.kdf.salt);
|
||||||
|
|
||||||
|
const kdf = await pbkdf2(
|
||||||
|
utf8ToBytes(password.normalize("NFKC")),
|
||||||
|
salt,
|
||||||
|
note.kdf.c,
|
||||||
|
note.kdf.dklen,
|
||||||
|
"sha256"
|
||||||
|
);
|
||||||
|
const decryptedContent = await decrypt(hexToBytes(note.content), kdf, iv);
|
||||||
|
|
||||||
|
return bytesToUtf8(decryptedContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notes = new Notes();
|
|
@ -1,6 +1,15 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { LightNode, createLightNode, waitForRemotePeer } from "@waku/sdk";
|
import { WakuStatus } from "@/const";
|
||||||
|
import {
|
||||||
|
IDecoder,
|
||||||
|
IEncoder,
|
||||||
|
IMessage,
|
||||||
|
LightNode,
|
||||||
|
createLightNode,
|
||||||
|
waitForRemotePeer,
|
||||||
|
IDecodedMessage,
|
||||||
|
} from "@waku/sdk";
|
||||||
|
|
||||||
type EventListener = (event: CustomEvent) => void;
|
type EventListener = (event: CustomEvent) => void;
|
||||||
|
|
||||||
|
@ -24,17 +33,17 @@ export class Waku {
|
||||||
|
|
||||||
this.initializing = true;
|
this.initializing = true;
|
||||||
try {
|
try {
|
||||||
this.emitStatusEvent("Initializing...");
|
this.emitStatusEvent(WakuStatus.Initializing);
|
||||||
const node = await createLightNode({ defaultBootstrap: true });
|
const node = await createLightNode({ defaultBootstrap: true });
|
||||||
await node.start();
|
await node.start();
|
||||||
this.emitStatusEvent("Waiting for peers...");
|
this.emitStatusEvent(WakuStatus.WaitingForPeers);
|
||||||
await waitForRemotePeer(node);
|
await waitForRemotePeer(node);
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
this.emitStatusEvent("Connected");
|
this.emitStatusEvent(WakuStatus.Connected);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to initialize Waku node:", error);
|
console.error("Failed to initialize Waku node:", error);
|
||||||
this.emitStatusEvent("Failed to initialize(see logs)");
|
this.emitStatusEvent(WakuStatus.Failed);
|
||||||
}
|
}
|
||||||
this.initializing = false;
|
this.initializing = false;
|
||||||
}
|
}
|
||||||
|
@ -47,11 +56,50 @@ export class Waku {
|
||||||
return this.emitter.removeEventListener(event, fn as any);
|
return this.emitter.removeEventListener(event, fn as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public send(encoder: IEncoder, message: IMessage) {
|
||||||
|
this.ensureWakuInitialized();
|
||||||
|
return this.node?.lightPush.send(encoder, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getHistory(
|
||||||
|
decoder: IDecoder<IDecodedMessage>
|
||||||
|
): Promise<IDecodedMessage[]> {
|
||||||
|
this.ensureWakuInitialized();
|
||||||
|
|
||||||
|
let messages: IDecodedMessage[] = [];
|
||||||
|
for await (const promises of this.node!.store.queryGenerator([decoder])) {
|
||||||
|
const messagesRaw = await Promise.all(promises);
|
||||||
|
const filteredMessages = messagesRaw.filter(
|
||||||
|
(v): v is IDecodedMessage => !!v
|
||||||
|
);
|
||||||
|
|
||||||
|
messages = [...messages, ...filteredMessages];
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async subscribe(
|
||||||
|
decoder: IDecoder<IDecodedMessage>,
|
||||||
|
fn: (m: IDecodedMessage) => void
|
||||||
|
) {
|
||||||
|
this.ensureWakuInitialized();
|
||||||
|
return this.node!.filter.subscribe(decoder, fn);
|
||||||
|
}
|
||||||
|
|
||||||
private emitStatusEvent(payload: string) {
|
private emitStatusEvent(payload: string) {
|
||||||
this.emitter.dispatchEvent(
|
this.emitter.dispatchEvent(
|
||||||
new CustomEvent(WakuEvents.Status, { detail: payload })
|
new CustomEvent(WakuEvents.Status, { detail: payload })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ensureWakuInitialized() {
|
||||||
|
if (!waku.initialized) {
|
||||||
|
const message = "Waku is not initialized.";
|
||||||
|
console.log(message);
|
||||||
|
throw Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const waku = new Waku();
|
export const waku = new Waku();
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
export function generateRandomString(): string {
|
||||||
|
let result = "";
|
||||||
|
let characters =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
let charactersLength = characters.length;
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
Loading…
Reference in New Issue