chore: add telemetry to buddy book and change text to use Book instea… (#110)
This commit is contained in:
parent
87d1499aa1
commit
7613b1e9e4
File diff suppressed because it is too large
Load Diff
|
@ -50,6 +50,7 @@
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@vitejs/plugin-react": "^4.3.2",
|
"@vitejs/plugin-react": "^4.3.2",
|
||||||
|
"@waku/interfaces": "^0.0.29-5674b0e.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
|
|
|
@ -206,15 +206,15 @@ const Home: React.FC = () => (
|
||||||
<div className="w-full max-w-sm mx-auto p-4 md:p-6 bg-card rounded-lg shadow-md">
|
<div className="w-full max-w-sm mx-auto p-4 md:p-6 bg-card rounded-lg shadow-md">
|
||||||
<Link to="create">
|
<Link to="create">
|
||||||
<Button className="w-full mb-4">
|
<Button className="w-full mb-4">
|
||||||
Create New Chain
|
Create New Book
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-sm md:text-base text-muted-foreground">
|
<p className="text-sm md:text-base text-muted-foreground">
|
||||||
Click the button above to start creating a new chain.
|
Click the button above to start creating a new book.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs md:text-sm text-muted-foreground text-center">
|
<p className="text-xs md:text-sm text-muted-foreground text-center">
|
||||||
Welcome to BuddyBook - Create and share your chains!
|
Welcome to BuddyBook - Create and share your books!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { useWaku } from '@waku/react';
|
||||||
import { LightNode } from '@waku/sdk';
|
import { LightNode } from '@waku/sdk';
|
||||||
import { createMessage, encoder } from '@/lib/waku';
|
import { createMessage, encoder } from '@/lib/waku';
|
||||||
import { useWalletPrompt } from '@/hooks/useWalletPrompt';
|
import { useWalletPrompt } from '@/hooks/useWalletPrompt';
|
||||||
|
import { fromLightPush, Telemetry, TelemetryType, buildExtraData, toInt } from '@/lib/telemetry';
|
||||||
|
|
||||||
interface FormData {
|
interface FormData {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -48,18 +49,49 @@ const ChainCreationForm: React.FC = () => {
|
||||||
const blockUUID = uuidv4();
|
const blockUUID = uuidv4();
|
||||||
setCreatedBlockUUID(blockUUID);
|
setCreatedBlockUUID(blockUUID);
|
||||||
|
|
||||||
|
const timestamp = Date.now();
|
||||||
const message = createMessage({
|
const message = createMessage({
|
||||||
chainUUID: formData.uuid,
|
chainUUID: formData.uuid,
|
||||||
blockUUID: blockUUID,
|
blockUUID: blockUUID,
|
||||||
title: formData.title,
|
title: formData.title,
|
||||||
description: formData.description,
|
description: formData.description,
|
||||||
signedMessage: signature,
|
signedMessage: signature,
|
||||||
timestamp: Date.now(),
|
timestamp: timestamp,
|
||||||
signatures: [{address, signature}],
|
signatures: [{address, signature}],
|
||||||
parentBlockUUID: null
|
parentBlockUUID: null
|
||||||
});
|
});
|
||||||
|
|
||||||
await node?.lightPush.send(encoder, message)
|
try {
|
||||||
|
const result = await node?.lightPush.send(encoder, message);
|
||||||
|
Telemetry.push(fromLightPush({
|
||||||
|
result,
|
||||||
|
wallet: address,
|
||||||
|
bookId: formData.uuid,
|
||||||
|
node,
|
||||||
|
encoder,
|
||||||
|
timestamp,
|
||||||
|
}));
|
||||||
|
} catch (e) {
|
||||||
|
Telemetry.push([{
|
||||||
|
type: TelemetryType.LIGHT_PUSH_FILTER,
|
||||||
|
protocol: "lightPush",
|
||||||
|
timestamp: toInt(timestamp),
|
||||||
|
createdAt: toInt(timestamp),
|
||||||
|
seenTimestamp: toInt(timestamp),
|
||||||
|
peerId: node.peerId.toString(),
|
||||||
|
contentTopic: encoder.contentTopic,
|
||||||
|
pubsubTopic: encoder.pubsubTopic,
|
||||||
|
ephemeral: encoder.ephemeral,
|
||||||
|
messageHash: uuidv4(),
|
||||||
|
errorMessage: (e as Error)?.message ?? "Error during LightPush",
|
||||||
|
extraData: buildExtraData({
|
||||||
|
wallet: address,
|
||||||
|
bookId: formData.uuid,
|
||||||
|
}),
|
||||||
|
}]);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
setIsSuccess(true);
|
setIsSuccess(true);
|
||||||
setIsSigning(false);
|
setIsSigning(false);
|
||||||
},
|
},
|
||||||
|
@ -112,8 +144,8 @@ const ChainCreationForm: React.FC = () => {
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
setIsSigning(true);
|
setIsSigning(true);
|
||||||
setSendError(null);
|
setSendError(null);
|
||||||
const message = `Create Chain:
|
const message = `Create Book:
|
||||||
Chain UUID: ${formData.uuid}
|
Book UUID: ${formData.uuid}
|
||||||
Title: ${formData.title}
|
Title: ${formData.title}
|
||||||
Description: ${formData.description}
|
Description: ${formData.description}
|
||||||
Timestamp: ${new Date().getTime()}
|
Timestamp: ${new Date().getTime()}
|
||||||
|
@ -132,12 +164,12 @@ const ChainCreationForm: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Card className="w-full max-w-2xl mx-auto">
|
<Card className="w-full max-w-2xl mx-auto">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Create a New Chain</CardTitle>
|
<CardTitle>Create a New Book</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<form onSubmit={handleCreateChain} className="space-y-6">
|
<form onSubmit={handleCreateChain} className="space-y-6">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="title">Chain Title</Label>
|
<Label htmlFor="title">Book Title</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="title"
|
id="title"
|
||||||
|
@ -150,7 +182,7 @@ const ChainCreationForm: React.FC = () => {
|
||||||
{errors.title && <p className="text-sm text-destructive">{errors.title}</p>}
|
{errors.title && <p className="text-sm text-destructive">{errors.title}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="description">Chain Description</Label>
|
<Label htmlFor="description">Book Description</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
id="description"
|
id="description"
|
||||||
name="description"
|
name="description"
|
||||||
|
@ -161,13 +193,13 @@ const ChainCreationForm: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
{errors.description && <p className="text-sm text-destructive">{errors.description}</p>}
|
{errors.description && <p className="text-sm text-destructive">{errors.description}</p>}
|
||||||
</div>
|
</div>
|
||||||
<Button type="submit" className="w-full py-6 text-base sm:py-2 sm:text-sm">Create Chain</Button>
|
<Button type="submit" className="w-full py-6 text-base sm:py-2 sm:text-sm">Create Book</Button>
|
||||||
</form>
|
</form>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<Dialog open={showModal} onOpenChange={handleCloseModal}>
|
<Dialog open={showModal} onOpenChange={handleCloseModal}>
|
||||||
<DialogContent className="sm:max-w-md">
|
<DialogContent className="sm:max-w-md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{isSuccess ? "Chain Created" : "Chain Preview"}</DialogTitle>
|
<DialogTitle>{isSuccess ? "Book Created" : "Book Preview"}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{!isSuccess ? (
|
{!isSuccess ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { Loader2 } from "lucide-react";
|
||||||
import QRCode from '@/components/QRCode';
|
import QRCode from '@/components/QRCode';
|
||||||
import { useWalletPrompt } from '@/hooks/useWalletPrompt';
|
import { useWalletPrompt } from '@/hooks/useWalletPrompt';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { fromLightPush, Telemetry } from '@/lib/telemetry';
|
||||||
|
|
||||||
interface SignChainProps {
|
interface SignChainProps {
|
||||||
block: BlockPayload;
|
block: BlockPayload;
|
||||||
|
@ -76,19 +77,31 @@ const SignChain: React.FC<SignChainProps> = ({ block, chainsData, onSuccess }) =
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const timestamp = Date.now();
|
||||||
const newBlock: BlockPayload = {
|
const newBlock: BlockPayload = {
|
||||||
chainUUID: block.chainUUID,
|
chainUUID: block.chainUUID,
|
||||||
blockUUID: uuidv4(),
|
blockUUID: uuidv4(),
|
||||||
title: block.title,
|
title: block.title,
|
||||||
description: block.description,
|
description: block.description,
|
||||||
signedMessage: signature,
|
signedMessage: signature,
|
||||||
timestamp: Date.now(),
|
timestamp,
|
||||||
signatures: [{ address, signature }],
|
signatures: [{ address, signature }],
|
||||||
parentBlockUUID: block.blockUUID
|
parentBlockUUID: block.blockUUID
|
||||||
};
|
};
|
||||||
|
|
||||||
const wakuMessage = createMessage(newBlock);
|
const wakuMessage = createMessage(newBlock);
|
||||||
const { failures, successes } = await node.lightPush.send(encoder, wakuMessage);
|
const result = await node.lightPush.send(encoder, wakuMessage);
|
||||||
|
|
||||||
|
Telemetry.push(fromLightPush({
|
||||||
|
result,
|
||||||
|
node,
|
||||||
|
encoder,
|
||||||
|
timestamp,
|
||||||
|
bookId: block.chainUUID,
|
||||||
|
wallet: address,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { failures, successes } = result;
|
||||||
|
|
||||||
if (failures.length > 0 || successes.length === 0) {
|
if (failures.length > 0 || successes.length === 0) {
|
||||||
throw new Error('Failed to send message to Waku network');
|
throw new Error('Failed to send message to Waku network');
|
||||||
|
@ -168,16 +181,16 @@ const SignChain: React.FC<SignChainProps> = ({ block, chainsData, onSuccess }) =
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={() => setIsOpen(true)} disabled={alreadySigned}>
|
<Button onClick={() => setIsOpen(true)} disabled={alreadySigned}>
|
||||||
{alreadySigned ? 'Already Signed' : !address ? 'Connect Wallet' : 'Sign Chain'}
|
{alreadySigned ? 'Already Signed' : !address ? 'Connect Wallet' : 'Sign Book'}
|
||||||
</Button>
|
</Button>
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogContent className="sm:max-w-md">
|
<DialogContent className="sm:max-w-md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Sign Chain</DialogTitle>
|
<DialogTitle>Sign Book</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{alreadySigned
|
{alreadySigned
|
||||||
? 'You have already signed this chain.'
|
? 'You have already signed this book.'
|
||||||
: 'Review the block details and sign to add your signature to the chain.'}
|
: 'Review the block details and sign to add your signature to the book.'}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex flex-col space-y-4">
|
<div className="flex flex-col space-y-4">
|
||||||
|
|
|
@ -29,7 +29,7 @@ const SignSharedChain: React.FC<SignSharedChainProps> = ({ chainsData, onChainUp
|
||||||
<Card className="w-full max-w-md mx-auto">
|
<Card className="w-full max-w-md mx-auto">
|
||||||
<CardContent className="flex flex-col items-center justify-center py-8 space-y-4">
|
<CardContent className="flex flex-col items-center justify-center py-8 space-y-4">
|
||||||
<Loader2 className="h-8 w-8 animate-spin" />
|
<Loader2 className="h-8 w-8 animate-spin" />
|
||||||
<p className="text-sm text-muted-foreground">Looking for chain...</p>
|
<p className="text-sm text-muted-foreground">Looking for book...</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@ -39,11 +39,11 @@ const SignSharedChain: React.FC<SignSharedChainProps> = ({ chainsData, onChainUp
|
||||||
return (
|
return (
|
||||||
<Card className="w-full max-w-md mx-auto">
|
<Card className="w-full max-w-md mx-auto">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Chain Not Found</CardTitle>
|
<CardTitle>Book Not Found</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<p className="mb-4">The requested chain or block could not be found.</p>
|
<p className="mb-4">The requested book or block could not be found.</p>
|
||||||
<Button onClick={() => navigate('/view')}>View All Chains</Button>
|
<Button onClick={() => navigate('/view')}>View All Books</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@ -52,7 +52,7 @@ const SignSharedChain: React.FC<SignSharedChainProps> = ({ chainsData, onChainUp
|
||||||
return (
|
return (
|
||||||
<Card className="w-full max-w-2xl mx-auto">
|
<Card className="w-full max-w-2xl mx-auto">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Sign Shared Chain</CardTitle>
|
<CardTitle>Sign Shared Book</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<h2 className="text-xl font-semibold mb-2">{block.title}</h2>
|
<h2 className="text-xl font-semibold mb-2">{block.title}</h2>
|
||||||
|
|
|
@ -66,9 +66,9 @@ const ChainList: React.FC<ChainListProps> = ({ chainsData, onChainUpdate, isLoad
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="flex flex-col gap-4">
|
<DialogContent className="flex flex-col gap-4">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Share Chain</DialogTitle>
|
<DialogTitle>Share this Book</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Share this chain with others to collect their signatures.
|
Share this book with others to collect their signatures.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
|
@ -110,18 +110,18 @@ const ChainList: React.FC<ChainListProps> = ({ chainsData, onChainUpdate, isLoad
|
||||||
<Card className="w-full max-w-4xl mx-auto">
|
<Card className="w-full max-w-4xl mx-auto">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
Existing Chains
|
Existing Books
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<span className="ml-2 inline-flex items-center text-muted-foreground text-sm font-normal">
|
<span className="ml-2 inline-flex items-center text-muted-foreground text-sm font-normal">
|
||||||
<Loader2 className="h-4 w-4 animate-spin mr-2" />
|
<Loader2 className="h-4 w-4 animate-spin mr-2" />
|
||||||
Loading more chains...
|
Loading more books...
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{rootBlocks.length === 0 && !isLoading ? (
|
{rootBlocks.length === 0 && !isLoading ? (
|
||||||
<p>No chains found.</p>
|
<p>No books found.</p>
|
||||||
) : (
|
) : (
|
||||||
<ul className="space-y-4">
|
<ul className="space-y-4">
|
||||||
{rootBlocks.map((block) => renderBlock(block, 0))}
|
{rootBlocks.map((block) => renderBlock(block, 0))}
|
||||||
|
|
|
@ -44,8 +44,8 @@ const ConnectionStatus: React.FC<ConnectionStatusProps> = ({ filter, store }) =>
|
||||||
return (
|
return (
|
||||||
<Card className="fixed bottom-4 left-4 right-4 md:static md:bottom-auto md:left-auto p-2 bg-background/80 backdrop-blur-sm border shadow-lg z-50 md:z-auto">
|
<Card className="fixed bottom-4 left-4 right-4 md:static md:bottom-auto md:left-auto p-2 bg-background/80 backdrop-blur-sm border shadow-lg z-50 md:z-auto">
|
||||||
<div className="flex flex-row justify-around md:justify-start md:gap-4">
|
<div className="flex flex-row justify-around md:justify-start md:gap-4">
|
||||||
<StatusIndicator status={filter} label="Filter" />
|
<StatusIndicator status={filter} label="Connection" />
|
||||||
<StatusIndicator status={store} label="Store" />
|
<StatusIndicator status={store} label="History" />
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
|
@ -86,14 +86,14 @@ const Header: React.FC<HeaderProps> = ({ wakuStatus }) => {
|
||||||
{!isWakuLoading && !wakuError && (
|
{!isWakuLoading && !wakuError && (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<span className="hidden md:inline text-muted-foreground">Filter:</span>
|
<span className="hidden md:inline text-muted-foreground">Connection:</span>
|
||||||
<div className={`w-2 h-2 md:w-3 md:h-3 rounded-full ${getStatusColor(wakuStatus.filter)}`}></div>
|
<div className={`w-2 h-2 md:w-3 md:h-3 rounded-full ${getStatusColor(wakuStatus.filter)}`}></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<span className="hidden md:inline text-muted-foreground">Store:</span>
|
<span className="hidden md:inline text-muted-foreground">History:</span>
|
||||||
<div className={`w-2 h-2 md:w-3 md:h-3 rounded-full ${getStatusColor(wakuStatus.store)}`}></div>
|
<div className={`w-2 h-2 md:w-3 md:h-3 rounded-full ${getStatusColor(wakuStatus.store)}`}></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1 hidden">
|
||||||
<span className="hidden md:inline text-muted-foreground">Peers:</span>
|
<span className="hidden md:inline text-muted-foreground">Peers:</span>
|
||||||
{isWakuLoading ? (
|
{isWakuLoading ? (
|
||||||
<Loader2 className="h-4 w-4 animate-spin" />
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
import { IDecoder, IEncoder, LightNode, SDKProtocolResult, SubscribeResult } from "@waku/interfaces";
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
export enum TelemetryType {
|
||||||
|
LIGHT_PUSH_FILTER = "LightPushFilter",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TelemetryMessage {
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
timestamp: number;
|
||||||
|
contentTopic: string;
|
||||||
|
pubsubTopic: string;
|
||||||
|
peerId: string;
|
||||||
|
errorMessage: string;
|
||||||
|
extraData: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TelemetryPushFilter extends TelemetryMessage {
|
||||||
|
type: "LightPushFilter",
|
||||||
|
protocol: string;
|
||||||
|
ephemeral: boolean;
|
||||||
|
seenTimestamp: number;
|
||||||
|
createdAt: number;
|
||||||
|
messageHash: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TelemetryClient {
|
||||||
|
constructor(
|
||||||
|
private readonly url: string,
|
||||||
|
private intervalPeriod: number = 5000
|
||||||
|
) {
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private queue: TelemetryMessage[] = [];
|
||||||
|
private intervalId: NodeJS.Timeout | null = null;
|
||||||
|
private requestId = 0;
|
||||||
|
|
||||||
|
public push<T extends TelemetryMessage>(messages: T[]) {
|
||||||
|
this.queue.push(...messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start() {
|
||||||
|
if (!this.intervalId) {
|
||||||
|
this.intervalId = setInterval(async () => {
|
||||||
|
if (this.queue.length > 0) {
|
||||||
|
const success = await this.send(this.queue);
|
||||||
|
if (success) {
|
||||||
|
console.log("Sent ", this.queue.length, " telemetry logs");
|
||||||
|
this.queue = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, this.intervalPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public stop() {
|
||||||
|
if (this.intervalId) {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
this.intervalId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async send<T extends TelemetryMessage>(messages: T[]) {
|
||||||
|
const isTelemetryOn = localStorage.getItem("telemetryOptIn");
|
||||||
|
if (!isTelemetryOn || isTelemetryOn === "false" || isTelemetryOn !== "true" || !window.location.hostname.includes("buddybook.fun")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const telemetryRequests = messages.map((message) => ({
|
||||||
|
id: ++this.requestId,
|
||||||
|
telemetryType: message.type.toString(),
|
||||||
|
telemetryData: message
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(this.url, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(telemetryRequests),
|
||||||
|
});
|
||||||
|
if (res.status !== 201) {
|
||||||
|
console.log("DEBUG: Error sending messages to telemetry service: ", res.status, res.statusText, res.json);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.log("DEBUG: Error sending messages to telemetry service", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Telemetry = new TelemetryClient("https://telemetry.status.im/waku-metrics", 5000);
|
||||||
|
|
||||||
|
type ExtraData = {
|
||||||
|
wallet?: string;
|
||||||
|
bookId?: string;
|
||||||
|
timeTaken?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildExtraData = ({
|
||||||
|
wallet,
|
||||||
|
bookId,
|
||||||
|
timeTaken,
|
||||||
|
}: ExtraData): string => {
|
||||||
|
return JSON.stringify({
|
||||||
|
sdk: "@waku/react:0.0.7-9a7287d",
|
||||||
|
wallet,
|
||||||
|
bookId,
|
||||||
|
timeTaken,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
type FromLightPush = {
|
||||||
|
node: LightNode,
|
||||||
|
timestamp: number,
|
||||||
|
encoder: IEncoder,
|
||||||
|
wallet: string,
|
||||||
|
bookId: string,
|
||||||
|
result: SDKProtocolResult,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fromLightPush = (data: FromLightPush): TelemetryPushFilter[] => {
|
||||||
|
const telemetry: TelemetryPushFilter[] = [];
|
||||||
|
|
||||||
|
data.result?.successes?.forEach((success) => {
|
||||||
|
telemetry.push({
|
||||||
|
type: TelemetryType.LIGHT_PUSH_FILTER,
|
||||||
|
protocol: "lightPush",
|
||||||
|
timestamp: data.timestamp,
|
||||||
|
createdAt: data.timestamp,
|
||||||
|
seenTimestamp: data.timestamp,
|
||||||
|
peerId: success.toString(),
|
||||||
|
contentTopic: data.encoder.contentTopic,
|
||||||
|
pubsubTopic: data.encoder.pubsubTopic,
|
||||||
|
ephemeral: false,
|
||||||
|
messageHash: uuidv4(),
|
||||||
|
errorMessage: "",
|
||||||
|
extraData: buildExtraData({
|
||||||
|
bookId: data.bookId,
|
||||||
|
wallet: data.wallet,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
data.result?.failures?.forEach((fail) => {
|
||||||
|
telemetry.push({
|
||||||
|
type: TelemetryType.LIGHT_PUSH_FILTER,
|
||||||
|
protocol: "lightPush",
|
||||||
|
timestamp: data.timestamp,
|
||||||
|
createdAt: data.timestamp,
|
||||||
|
seenTimestamp: data.timestamp,
|
||||||
|
peerId: fail?.peerId?.toString() || "missing",
|
||||||
|
contentTopic: data.encoder.contentTopic,
|
||||||
|
pubsubTopic: data.encoder.pubsubTopic,
|
||||||
|
ephemeral: data.encoder.ephemeral,
|
||||||
|
messageHash: uuidv4(),
|
||||||
|
errorMessage: fail.error.toString(),
|
||||||
|
extraData: buildExtraData({
|
||||||
|
wallet: data.wallet,
|
||||||
|
bookId: data.bookId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return telemetry;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FromFilter = {
|
||||||
|
result: SubscribeResult,
|
||||||
|
node: LightNode,
|
||||||
|
timestamp: number,
|
||||||
|
decoder: IDecoder<any>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fromFilter = (data: FromFilter): TelemetryPushFilter[] => {
|
||||||
|
const telemetry: TelemetryPushFilter[] = [];
|
||||||
|
const { error, results } = data.result;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
telemetry.push({
|
||||||
|
type: TelemetryType.LIGHT_PUSH_FILTER,
|
||||||
|
protocol: "filter",
|
||||||
|
timestamp: toInt(data.timestamp),
|
||||||
|
createdAt: toInt(data.timestamp),
|
||||||
|
seenTimestamp: toInt(data.timestamp),
|
||||||
|
peerId: data.node.peerId.toString(),
|
||||||
|
contentTopic: data.decoder.contentTopic,
|
||||||
|
pubsubTopic: data.decoder.pubsubTopic,
|
||||||
|
ephemeral: false,
|
||||||
|
messageHash: uuidv4(),
|
||||||
|
errorMessage: error,
|
||||||
|
extraData: buildExtraData({}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
results?.failures?.forEach((fail) => {
|
||||||
|
telemetry.push({
|
||||||
|
type: TelemetryType.LIGHT_PUSH_FILTER,
|
||||||
|
protocol: "filter",
|
||||||
|
timestamp: toInt(data.timestamp),
|
||||||
|
createdAt: toInt(data.timestamp),
|
||||||
|
seenTimestamp: toInt(data.timestamp),
|
||||||
|
peerId: fail?.peerId?.toString() || "",
|
||||||
|
contentTopic: data.decoder.contentTopic,
|
||||||
|
pubsubTopic: data.decoder.pubsubTopic,
|
||||||
|
ephemeral: false,
|
||||||
|
messageHash: uuidv4(),
|
||||||
|
errorMessage: fail?.error || "Unknown error",
|
||||||
|
extraData: buildExtraData({}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
results?.successes?.forEach((success) => {
|
||||||
|
telemetry.push({
|
||||||
|
type: TelemetryType.LIGHT_PUSH_FILTER,
|
||||||
|
protocol: "filter",
|
||||||
|
timestamp: toInt(data.timestamp),
|
||||||
|
createdAt: toInt(data.timestamp),
|
||||||
|
seenTimestamp: toInt(data.timestamp),
|
||||||
|
peerId: success.toString(),
|
||||||
|
contentTopic: data.decoder.contentTopic,
|
||||||
|
pubsubTopic: data.decoder.pubsubTopic,
|
||||||
|
ephemeral: false,
|
||||||
|
messageHash: uuidv4(),
|
||||||
|
errorMessage: "",
|
||||||
|
extraData: buildExtraData({}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return telemetry;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FromStore = {
|
||||||
|
timestamp: number,
|
||||||
|
timeTaken: number,
|
||||||
|
node: LightNode,
|
||||||
|
decoder: IDecoder<any>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fromStore = (data: FromStore): TelemetryPushFilter[] => {
|
||||||
|
return [{
|
||||||
|
type: TelemetryType.LIGHT_PUSH_FILTER,
|
||||||
|
protocol: "filter",
|
||||||
|
timestamp: toInt(data.timestamp),
|
||||||
|
createdAt: toInt(data.timestamp),
|
||||||
|
seenTimestamp: toInt(data.timestamp),
|
||||||
|
peerId: data.node.peerId.toString(),
|
||||||
|
contentTopic: data.decoder.contentTopic,
|
||||||
|
pubsubTopic: data.decoder.pubsubTopic,
|
||||||
|
ephemeral: false,
|
||||||
|
messageHash: uuidv4(),
|
||||||
|
errorMessage: "",
|
||||||
|
extraData: buildExtraData({
|
||||||
|
timeTaken: data.timeTaken
|
||||||
|
}),
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function toInt(v: any): number {
|
||||||
|
return parseInt(v);
|
||||||
|
}
|
|
@ -1,8 +1,15 @@
|
||||||
import { createEncoder, createDecoder, type LightNode, type CreateWakuNodeOptions } from "@waku/sdk";
|
import { createEncoder, createDecoder, type LightNode } from "@waku/sdk";
|
||||||
|
import { type CreateWakuNodeOptions } from "@waku/sdk";
|
||||||
import protobuf from 'protobufjs';
|
import protobuf from 'protobufjs';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { Telemetry, fromFilter, fromStore, TelemetryType, buildExtraData, toInt } from "./telemetry";
|
||||||
|
|
||||||
export const WAKU_NODE_OPTIONS: CreateWakuNodeOptions = { defaultBootstrap: true, nodeToUse: {store: "/dns4/boot-01.do-ams3.status.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmEqqio4UR1SWqAc7KY19t6qyDvtmyjreZpzUBJvb4u65R"} };
|
export const WAKU_NODE_OPTIONS: CreateWakuNodeOptions = {
|
||||||
|
defaultBootstrap: true,
|
||||||
|
nodeToUse: {
|
||||||
|
store: "/dns4/node-01.ac-cn-hongkong-c.waku.test.status.im/tcp/8000/wss/p2p/16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export type Signature = {
|
export type Signature = {
|
||||||
address: `0x${string}`;
|
address: `0x${string}`;
|
||||||
|
@ -64,35 +71,67 @@ export function createMessage({
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function* getMessagesFromStore(node: LightNode) {
|
export async function* getMessagesFromStore(node: LightNode) {
|
||||||
console.time("getMessagesFromStore")
|
const startTime = performance.now();
|
||||||
try {
|
try {
|
||||||
for await (const messagePromises of node.store.queryGenerator([decoder])) {
|
for await (const messagePromises of node.store.queryGenerator([decoder])) {
|
||||||
const messages = await Promise.all(messagePromises);
|
const messages = await Promise.all(messagePromises);
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
|
console.log(message)
|
||||||
if (!message?.payload) continue;
|
if (!message?.payload) continue;
|
||||||
const blockPayload = block.decode(message.payload) as unknown as BlockPayload;
|
const blockPayload = block.decode(message.payload) as unknown as BlockPayload;
|
||||||
blockPayload.signatures = blockPayload.signatures.map(s => JSON.parse(s as unknown as string) as Signature);
|
blockPayload.signatures = blockPayload.signatures.map(s => JSON.parse(s as unknown as string) as Signature);
|
||||||
yield blockPayload;
|
yield blockPayload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
const endTime = performance.now();
|
||||||
console.timeEnd("getMessagesFromStore")
|
const timeTaken = endTime - startTime;
|
||||||
|
console.log("getMessagesFromStore", timeTaken)
|
||||||
|
|
||||||
|
Telemetry.push(fromStore({
|
||||||
|
node,
|
||||||
|
decoder,
|
||||||
|
timestamp: startTime,
|
||||||
|
timeTaken,
|
||||||
|
}));
|
||||||
|
} catch(e) {
|
||||||
|
const endTime = performance.now();
|
||||||
|
const timeTaken = endTime - startTime;
|
||||||
|
Telemetry.push([{
|
||||||
|
type: TelemetryType.LIGHT_PUSH_FILTER,
|
||||||
|
protocol: "lightPush",
|
||||||
|
timestamp: toInt(startTime),
|
||||||
|
createdAt: toInt(startTime),
|
||||||
|
seenTimestamp: toInt(startTime),
|
||||||
|
peerId: node.peerId.toString(),
|
||||||
|
contentTopic: encoder.contentTopic,
|
||||||
|
pubsubTopic: encoder.pubsubTopic,
|
||||||
|
ephemeral: encoder.ephemeral,
|
||||||
|
messageHash: uuidv4(),
|
||||||
|
errorMessage: (e as Error)?.message ?? "Error during Store",
|
||||||
|
extraData: buildExtraData({ timeTaken }),
|
||||||
|
}]);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function subscribeToFilter(node: LightNode, callback: (message: BlockPayload) => void) {
|
export async function subscribeToFilter(node: LightNode, callback: (message: BlockPayload) => void) {
|
||||||
const {error, subscription, results} = await node.filter.subscribe(
|
const result = await node.filter.subscribe([decoder], (message) => {
|
||||||
[decoder],
|
|
||||||
(message) => {
|
|
||||||
console.log('message received from filter', message)
|
console.log('message received from filter', message)
|
||||||
if (message.payload) {
|
if (message.payload) {
|
||||||
const blockPayload = block.decode(message.payload) as unknown as BlockPayload;
|
const blockPayload = block.decode(message.payload) as unknown as BlockPayload;
|
||||||
blockPayload.signatures = blockPayload.signatures.map(s => JSON.parse(s as unknown as string) as Signature);
|
blockPayload.signatures = blockPayload.signatures.map(s => JSON.parse(s as unknown as string) as Signature);
|
||||||
callback(blockPayload);
|
callback(blockPayload);
|
||||||
}
|
}
|
||||||
}
|
}, {forceUseAllPeers: false});
|
||||||
);
|
|
||||||
|
|
||||||
|
Telemetry.push(fromFilter({
|
||||||
|
result,
|
||||||
|
node,
|
||||||
|
decoder,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const {error, subscription, results} = result;
|
||||||
console.log("results", results)
|
console.log("results", results)
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
Loading…
Reference in New Issue