mirror of
https://github.com/smolgrrr/TAO.git
synced 2024-09-20 01:11:25 +00:00
sort by work feed
This commit is contained in:
parent
8231f7bf93
commit
20df7f8507
11
README.md
11
README.md
@ -1,4 +1,4 @@
|
|||||||
# The Wire: unstoppable free speech
|
# The Wired: unstoppable free speech
|
||||||
An app to facilitate unstoppable free speech on the internet.
|
An app to facilitate unstoppable free speech on the internet.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@ -6,10 +6,5 @@ Nostr at the core: censorship resistant, also stupid simple and permissionless <
|
|||||||
Anon only: Names and Nyms carry reputations/ego and risk doxxing - Anons speak freely <br/>
|
Anon only: Names and Nyms carry reputations/ego and risk doxxing - Anons speak freely <br/>
|
||||||
PoW: Spam and noise
|
PoW: Spam and noise
|
||||||
|
|
||||||
### Wanting to add
|
## Back up
|
||||||
|
https://git.getwired.app/doot/TAO
|
||||||
Ways to make Nostr notes impossible to stop? <br/>
|
|
||||||
- Secure Scuttlebutt gossip and offline (?): Even in locked down countries, if we have a SSB-like medium, we only need one person to connect to a relay to share other ppl's messages<br/>
|
|
||||||
- Extremely lightweight relays and gossiping <br/>
|
|
||||||
- PWAs and bridges: A proliferation of similar, simple PWAs, and the sharing of notes through any means (telegram, email, pigeon) before getting to nostr <br/>
|
|
||||||
- API connection to do PoW externally to app if wanted
|
|
@ -2,7 +2,7 @@ import { PropsWithChildren } from "react";
|
|||||||
|
|
||||||
export default function CardContainer({ children }: PropsWithChildren) {
|
export default function CardContainer({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<div className="card break-inside-avoid mb-4 h-min">
|
<div className="card break-inside-avoid mb-1 h-min">
|
||||||
<div className="card-body">{children}</div>
|
<div className="card-body">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -31,10 +31,30 @@ const PostCard = ({
|
|||||||
const icon = getIconFromHash(event.pubkey);
|
const icon = getIconFromHash(event.pubkey);
|
||||||
const metadataParsed = metadata ? getMetadata(metadata) : null;
|
const metadataParsed = metadata ? getMetadata(metadata) : null;
|
||||||
const [relatedEvents, setRelatedEvents] = useState<Event[]>([]);
|
const [relatedEvents, setRelatedEvents] = useState<Event[]>([]);
|
||||||
|
const [sumReplyPow, setReplySumPow] = useState(0);
|
||||||
|
const [repostedEvent, setRepostedEvent] = useState<Event>();
|
||||||
|
const [parsedEvent, setParsedEvent] = useState<Event>(event);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const allRelatedEvents = [event, ...(replies || [])];
|
const allRelatedEvents = [event, ...(replies || [])];
|
||||||
setRelatedEvents(allRelatedEvents);
|
setRelatedEvents(allRelatedEvents);
|
||||||
|
|
||||||
|
if (event.kind === 6) {
|
||||||
|
setRepostedEvent(event)
|
||||||
|
setParsedEvent(JSON.parse(event.content));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjusting the sum calculation to account for exponential growth in work
|
||||||
|
const sum = replies.reduce((acc, reply) => {
|
||||||
|
const difficulty = verifyPow(reply);
|
||||||
|
// Skip adding to the sum if difficulty is 0, assuming 0 means no work was done.
|
||||||
|
// Adjust this logic if verifyPow uses a different scale or interpretation.
|
||||||
|
return difficulty > 0 ? acc + Math.pow(2, difficulty) : acc;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
// Check if sum is greater than 0 to avoid -Infinity in log2 calculation
|
||||||
|
const equivalentDifficulty = sum > 0 ? Math.log2(sum) : 0;
|
||||||
|
setReplySumPow(equivalentDifficulty);
|
||||||
}, [event, replies]);
|
}, [event, replies]);
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
@ -44,20 +64,21 @@ const PostCard = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardContainer>
|
<CardContainer>
|
||||||
<div className={`flex flex-col gap-2`}>
|
<div className={`flex flex-col gap-2`} key={key}>
|
||||||
<div className={`flex flex-col break-words ${type !== "OP" ? 'hover:cursor-pointer' : ''}`} onClick={handleClick}>
|
<div className={`flex flex-col break-words ${type !== "OP" ? 'hover:cursor-pointer' : ''}`} onClick={handleClick}>
|
||||||
<ContentPreview key={event.id} eventdata={event} />
|
<ContentPreview key={parsedEvent.id} eventdata={parsedEvent} />
|
||||||
</div>
|
</div>
|
||||||
{repliedTo && <div className="flex items-center mt-1" >
|
{repliedTo && <div className="flex items-center mt-1" >
|
||||||
<span className="text-xs text-gray-500">Reply to: </span>
|
<span className="text-xs text-gray-500">Reply to: </span>
|
||||||
{uniqBy(repliedTo, 'pubkey').map((event, index) => (
|
{uniqBy(repliedTo, 'pubkey').map((parsedEvent, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
{event.kind === 0 ? (
|
{event.kind === 0 ? (
|
||||||
<img className={`h-5 w-5 rounded-full`} src={getMetadata(event)?.picture} />
|
<img className={`h-5 w-5 rounded-full`} src={getMetadata(parsedEvent)?.picture} />
|
||||||
) : (
|
) : (
|
||||||
<div className={`h-4 w-4 ${getIconFromHash(event.pubkey)} rounded-full`} />
|
<div className={`h-4 w-4 ${getIconFromHash(parsedEvent.pubkey)} rounded-full`} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -75,18 +96,26 @@ const PostCard = ({
|
|||||||
<div className={`h-4 w-4 ${icon} rounded-full`} />
|
<div className={`h-4 w-4 ${icon} rounded-full`} />
|
||||||
}
|
}
|
||||||
<div className="flex items-center ml-auto gap-2.5">
|
<div className="flex items-center ml-auto gap-2.5">
|
||||||
<div className="inline-flex text-xs text-neutral-600 gap-0.5">
|
<div className={`inline-flex text-xs ${verifyPow(parsedEvent) === 0 ? 'text-neutral-600' : 'text-sky-800'} gap-0.5`}>
|
||||||
<CpuChipIcon className="h-4 w-4" /> {verifyPow(event)}
|
<CpuChipIcon className="h-4 w-4" /> {verifyPow(parsedEvent)}
|
||||||
|
</div>
|
||||||
|
{repostedEvent &&
|
||||||
|
<div className={`inline-flex text-xs ${verifyPow(repostedEvent) === 0 ? 'text-neutral-600' : 'text-sky-800'}`}>
|
||||||
|
+ <CpuChipIcon className="h-4 w-4" /> {verifyPow(repostedEvent)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<span className="text-neutral-700">·</span>
|
||||||
|
<div className="min-w-20 inline-flex items-center text-neutral-600">
|
||||||
|
<FolderIcon className="h-4 w-4" />
|
||||||
|
<span className="text-xs pl-1">{replies.length}</span>
|
||||||
|
(
|
||||||
|
<CpuChipIcon className={`h-4 w-4 ${sumReplyPow === 0 ? 'text-neutral-600' : 'text-sky-800'}`} />
|
||||||
|
<span className={`text-xs ${sumReplyPow === 0 ? 'text-neutral-600' : 'text-sky-800'}`}>{sumReplyPow.toFixed(0)}</span>)
|
||||||
</div>
|
</div>
|
||||||
<span className="text-neutral-700">·</span>
|
<span className="text-neutral-700">·</span>
|
||||||
<div className="text-xs font-semibold text-neutral-600">
|
<div className="min-w-6 text-xs font-semibold text-neutral-600">
|
||||||
{timeAgo(event.created_at)}
|
{timeAgo(event.created_at)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-neutral-700">·</span>
|
|
||||||
<div className="inline-flex items-center gap-1">
|
|
||||||
<FolderIcon className="h-4 w-4 text-neutral-600" />
|
|
||||||
<span className="text-xs text-neutral-600">{replies.length}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
// import CardContainer from "./CardContainer";
|
|
||||||
import { CpuChipIcon } from "@heroicons/react/24/outline";
|
|
||||||
// import { parseContent } from "../../utils/content";
|
|
||||||
import { Event, nip19 } from "nostr-tools";
|
|
||||||
import { getMetadata, Metadata } from "../../utils/getMetadata";
|
|
||||||
// import { renderMedia } from "../../utils/FileUpload";
|
|
||||||
import { getIconFromHash, timeAgo } from "../../utils/cardUtils";
|
|
||||||
import { verifyPow } from "../../utils/mine";
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { subNoteOnce } from "../../utils/subscriptions";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import ContentPreview from "./CardModals/TextModal";
|
|
||||||
|
|
||||||
interface RepostProps {
|
|
||||||
key?: string | number;
|
|
||||||
event: Event;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RepostCard = ({
|
|
||||||
key,
|
|
||||||
event
|
|
||||||
}: RepostProps) => {
|
|
||||||
const repostedEvent = JSON.parse(event.content);
|
|
||||||
// const { files } = parseContent(repostedEvent);
|
|
||||||
const icon = getIconFromHash(event.pubkey);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [cachedMetadataEvents, setCachedMetadataEvents] = useState<Event[]>(
|
|
||||||
JSON.parse(localStorage.getItem("cachedMetadataEvents") || "[]")
|
|
||||||
);
|
|
||||||
const [metadata, setMetadata] = useState<Metadata>()
|
|
||||||
|
|
||||||
// Define your callback function for subGlobalFeed
|
|
||||||
const onEvent = (event: Event, relay: string) => {
|
|
||||||
const existingEvent = cachedMetadataEvents.find((e) => e.pubkey === event.pubkey)
|
|
||||||
if (existingEvent) {
|
|
||||||
setMetadata(getMetadata(existingEvent))
|
|
||||||
}
|
|
||||||
else if (!existingEvent && event.kind === 0 && event.pubkey === repostedEvent.pubkey && metadata == null) {
|
|
||||||
setMetadata(getMetadata(event))
|
|
||||||
|
|
||||||
setCachedMetadataEvents((prevMetadataEvents) => {
|
|
||||||
// Check if the event already exists in the cached metadata events
|
|
||||||
const existingEvent = prevMetadataEvents.find((e) => e.id === event.id || e.pubkey === event.pubkey)
|
|
||||||
if (!existingEvent) {
|
|
||||||
// If the event doesn't exist, add it to the cached metadata events
|
|
||||||
return [...prevMetadataEvents, event];
|
|
||||||
} else if (existingEvent && existingEvent.created_at < event.created_at) {
|
|
||||||
// Remove any existing metadata event with the same pubkey and id
|
|
||||||
const updatedMetadataEvents = prevMetadataEvents.filter(
|
|
||||||
(e) => e.id !== existingEvent.id
|
|
||||||
);
|
|
||||||
// Add the new metadata event
|
|
||||||
return [...updatedMetadataEvents, event];
|
|
||||||
}
|
|
||||||
// If the event already exists, return the previous cached metadata events
|
|
||||||
return prevMetadataEvents;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
subNoteOnce(repostedEvent.id, onEvent);
|
|
||||||
}, [repostedEvent.id]);
|
|
||||||
|
|
||||||
// Save the cached metadataEvents to localStorage
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem("cachedMetadataEvents", JSON.stringify(cachedMetadataEvents));
|
|
||||||
}, [cachedMetadataEvents]);
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
navigate(`/thread/${nip19.noteEncode(repostedEvent.id)}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div className="ml-1 flex text-sm text-neutral-600 gap-2.5">
|
|
||||||
Repost
|
|
||||||
@
|
|
||||||
<span className="inline-flex"><CpuChipIcon className="h-5 w-5" /> {verifyPow(event)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-lg border border-neutral-700">
|
|
||||||
<div className="card break-inside-avoid h-min">
|
|
||||||
<div className="card-body">
|
|
||||||
<div className={`flex flex-col gap-2`}>
|
|
||||||
<div className={`flex flex-col break-words hover:cursor-pointer`} onClick={handleClick}>
|
|
||||||
<ContentPreview key={repostedEvent.id} eventdata={repostedEvent} />
|
|
||||||
</div>
|
|
||||||
<div className={`flex justify-between items-center hover:cursor-pointer`} onClick={handleClick}>
|
|
||||||
{metadata ?
|
|
||||||
<img
|
|
||||||
key = {key}
|
|
||||||
className={`h-5 w-5 rounded-full`}
|
|
||||||
src={metadata?.picture ?? icon}
|
|
||||||
alt=""
|
|
||||||
loading="lazy"
|
|
||||||
decoding="async"/>
|
|
||||||
:
|
|
||||||
<div className={`h-4 w-4 ${icon} rounded-full`} />
|
|
||||||
}
|
|
||||||
<div className="flex items-center ml-auto gap-2.5">
|
|
||||||
<div className="inline-flex text-xs text-neutral-600 gap-0.5">
|
|
||||||
<CpuChipIcon className="h-4 w-4" /> {verifyPow(repostedEvent)}
|
|
||||||
</div>
|
|
||||||
<span className="text-neutral-700">·</span>
|
|
||||||
<div className="text-xs font-semibold text-neutral-600">
|
|
||||||
{timeAgo(repostedEvent.created_at)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RepostCard;
|
|
@ -2,7 +2,6 @@ import PostCard from "../modals/PostCard";
|
|||||||
import { verifyPow } from "../../utils/mine";
|
import { verifyPow } from "../../utils/mine";
|
||||||
import { Event } from "nostr-tools";
|
import { Event } from "nostr-tools";
|
||||||
import NewNoteCard from "../forms/PostFormCard";
|
import NewNoteCard from "../forms/PostFormCard";
|
||||||
import RepostCard from "../modals/RepostCard";
|
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useFetchEvents } from "../../hooks/useFetchEvents";
|
import { useFetchEvents } from "../../hooks/useFetchEvents";
|
||||||
|
|
||||||
@ -36,19 +35,15 @@ const HashtagPage = () => {
|
|||||||
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
||||||
<NewNoteCard hashtag={id as string} />
|
<NewNoteCard hashtag={id as string} />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4">
|
<div className="grid grid-cols-1 max-w-xl mx-auto gap-1 px-4">
|
||||||
{sortedEvents.map((event) => (
|
{sortedEvents.map((event) =>
|
||||||
event.kind === 1 ?
|
|
||||||
<PostCard
|
<PostCard
|
||||||
event={event}
|
event={event}
|
||||||
metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null}
|
metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null}
|
||||||
replies={sortedEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id))}
|
replies={sortedEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id))}
|
||||||
/>
|
/>
|
||||||
:
|
|
||||||
<RepostCard
|
)}
|
||||||
event={event}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { verifyPow } from "../../utils/mine";
|
import { verifyPow } from "../../utils/mine";
|
||||||
import { Event } from "nostr-tools";
|
import { Event } from "nostr-tools";
|
||||||
import NewNoteCard from "../forms/PostFormCard";
|
import NewNoteCard from "../forms/PostFormCard";
|
||||||
import RepostCard from "../modals/RepostCard";
|
|
||||||
import { DEFAULT_DIFFICULTY } from "../../config";
|
import { DEFAULT_DIFFICULTY } from "../../config";
|
||||||
import PostCard from "../modals/PostCard";
|
import PostCard from "../modals/PostCard";
|
||||||
import { useFetchEvents } from "../../hooks/useFetchEvents";
|
import { useFetchEvents } from "../../hooks/useFetchEvents";
|
||||||
@ -14,10 +13,29 @@ const Home = () => {
|
|||||||
.filter((event) =>
|
.filter((event) =>
|
||||||
verifyPow(event) >= Number(filterDifficulty) &&
|
verifyPow(event) >= Number(filterDifficulty) &&
|
||||||
event.kind !== 0 &&
|
event.kind !== 0 &&
|
||||||
(event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e" || tag[0] === "a"))
|
(event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e" || tag[0] === "p"))
|
||||||
)
|
)
|
||||||
|
|
||||||
const sortedEvents = postEvents.sort((a, b) => b.created_at - a.created_at);
|
const postEventsWithReplies = postEvents.map((event) => {
|
||||||
|
const totalWork = Math.pow(2, verifyPow(event))
|
||||||
|
+ noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id))
|
||||||
|
.reduce((acc, reply) => acc + Math.pow(2, verifyPow(reply)), 0);
|
||||||
|
return {
|
||||||
|
postEvent: event,
|
||||||
|
replies: noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id)),
|
||||||
|
totalWork: totalWork, // Add total work here
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedEvents = postEventsWithReplies
|
||||||
|
.sort((a, b) => {
|
||||||
|
// Sort by total work in descending order
|
||||||
|
const workDiff = b.totalWork - a.totalWork;
|
||||||
|
if (workDiff !== 0) return workDiff;
|
||||||
|
|
||||||
|
// If total work is the same, sort by created_at in descending order
|
||||||
|
return b.postEvent.created_at - a.postEvent.created_at;
|
||||||
|
});
|
||||||
|
|
||||||
// Render the component
|
// Render the component
|
||||||
return (
|
return (
|
||||||
@ -25,17 +43,13 @@ const Home = () => {
|
|||||||
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
||||||
<NewNoteCard />
|
<NewNoteCard />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4">
|
<div className="grid grid-cols-1 max-w-xl mx-auto gap-1 px-4">
|
||||||
{sortedEvents.map((event) => (
|
{sortedEvents.map((event) => (
|
||||||
event.kind === 1 ?
|
|
||||||
<PostCard
|
<PostCard
|
||||||
event={event}
|
key={event.postEvent.id}
|
||||||
metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null}
|
event={event.postEvent}
|
||||||
replies={noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id))}
|
metadata={metadataEvents.find((e) => e.pubkey === event.postEvent.pubkey && e.kind === 0) || null}
|
||||||
/>
|
replies={event.replies}
|
||||||
:
|
|
||||||
<RepostCard
|
|
||||||
event={event}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useState, useCallback } from "react";
|
import { useState, useCallback } from "react";
|
||||||
import PostCard from "../modals/PostCard";
|
import PostCard from "../modals/PostCard";
|
||||||
import { Event } from "nostr-tools";
|
import { Event } from "nostr-tools";
|
||||||
import RepostCard from "../modals/RepostCard";
|
|
||||||
import { useFetchEvents } from "../../hooks/useFetchEvents";
|
import { useFetchEvents } from "../../hooks/useFetchEvents";
|
||||||
|
|
||||||
const Notifications = () => {
|
const Notifications = () => {
|
||||||
@ -65,31 +64,21 @@ const Notifications = () => {
|
|||||||
<div className={`grid grid-cols-1 gap-4 px-4 flex-grow ${notifsView ? 'hidden sm:block' : ''}`}>
|
<div className={`grid grid-cols-1 gap-4 px-4 flex-grow ${notifsView ? 'hidden sm:block' : ''}`}>
|
||||||
<span>Your Recent Posts</span>
|
<span>Your Recent Posts</span>
|
||||||
{sortedEvents.map((event) => (
|
{sortedEvents.map((event) => (
|
||||||
event.kind === 1 ?
|
|
||||||
<PostCard
|
<PostCard
|
||||||
event={event}
|
event={event}
|
||||||
metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null}
|
metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null}
|
||||||
replies={countReplies(event)}
|
replies={countReplies(event)}
|
||||||
/>
|
/>
|
||||||
:
|
|
||||||
<RepostCard
|
|
||||||
event={event}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className={`grid grid-cols-1 gap-4 px-4 flex-grow ${notifsView ? '' : 'hidden sm:block'}`}>
|
<div className={`grid grid-cols-1 gap-4 px-4 flex-grow ${notifsView ? '' : 'hidden sm:block'}`}>
|
||||||
<span>Mentions</span>
|
<span>Mentions</span>
|
||||||
{sortedMentions.map((event) => (
|
{sortedMentions.map((event) => (
|
||||||
event.kind === 1 ?
|
|
||||||
<PostCard
|
<PostCard
|
||||||
event={event}
|
event={event}
|
||||||
metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null}
|
metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null}
|
||||||
replies={countReplies(event)}
|
replies={countReplies(event)}
|
||||||
/>
|
/>
|
||||||
:
|
|
||||||
<RepostCard
|
|
||||||
event={event}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -67,7 +67,7 @@ const Thread = () => {
|
|||||||
.sort((a, b) => a.created_at - b.created_at).map((event, index) => (
|
.sort((a, b) => a.created_at - b.created_at).map((event, index) => (
|
||||||
<PostCard event={event} metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null} replies={countReplies(event)} />
|
<PostCard event={event} metadata={metadataEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null} replies={countReplies(event)} />
|
||||||
))}
|
))}
|
||||||
<PostCard event={OPEvent} metadata={metadataEvents.find((e) => e.pubkey === OPEvent.pubkey && e.kind === 0) || null} replies={countReplies(OPEvent)} type={'OP'}/>
|
<PostCard event={OPEvent} metadata={metadataEvents.find((e) => e.pubkey === OPEvent.pubkey && e.kind === 0) || null} replies={replyEvents} type={'OP'}/>
|
||||||
</div>
|
</div>
|
||||||
<ThreadPostModal OPEvent={OPEvent} />
|
<ThreadPostModal OPEvent={OPEvent} />
|
||||||
<div className="col-span-full h-0.5 bg-neutral-900"/> {/* This is the white line separator */}
|
<div className="col-span-full h-0.5 bg-neutral-900"/> {/* This is the white line separator */}
|
||||||
|
2
pow_server/.gitignore
vendored
2
pow_server/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
go.sum
|
|
||||||
main
|
|
@ -1,23 +0,0 @@
|
|||||||
# Start from the latest golang base image
|
|
||||||
FROM golang:latest
|
|
||||||
|
|
||||||
# Set the Current Working Directory inside the container
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy go mod and sum files
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
|
|
||||||
# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
# Copy the source from the current directory to the Working Directory inside the container
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Build the Go app
|
|
||||||
RUN go build -o main .
|
|
||||||
|
|
||||||
# Expose port 8080 to the outside world
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
# Command to run the executable
|
|
||||||
CMD ["./main"]
|
|
@ -1,27 +0,0 @@
|
|||||||
module pow_server
|
|
||||||
|
|
||||||
go 1.21.4
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
|
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
|
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
|
||||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
|
||||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
|
||||||
github.com/gobwas/httphead v0.1.0 // indirect
|
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
|
||||||
github.com/gobwas/ws v1.2.0 // indirect
|
|
||||||
github.com/golang/glog v1.0.0 // indirect
|
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
|
||||||
github.com/nbd-wtf/go-nostr v0.25.7 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
|
|
||||||
github.com/tidwall/gjson v1.14.4 // indirect
|
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
|
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
|
||||||
)
|
|
@ -1,54 +0,0 @@
|
|||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
|
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM=
|
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
|
||||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
|
||||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
|
||||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
|
||||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
|
||||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
|
||||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
|
||||||
github.com/gobwas/ws v1.2.0 h1:u0p9s3xLYpZCA1z5JgCkMeB34CKCMMQbM+G8Ii7YD0I=
|
|
||||||
github.com/gobwas/ws v1.2.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
|
||||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
|
||||||
github.com/nbd-wtf/go-nostr v0.25.7 h1:DcGOSgKVr/L6w62tRtKeV2t46sRyFcq9pWcyIFkh0eM=
|
|
||||||
github.com/nbd-wtf/go-nostr v0.25.7/go.mod h1:bkffJI+x914sPQWum9ZRUn66D7NpDnAoWo1yICvj3/0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU=
|
|
||||||
github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
|
||||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
|
||||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
|
||||||
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
|
|
||||||
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
|
||||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -1,225 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"math/bits"
|
|
||||||
"net/http"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrDifficultyTooLow = errors.New("nip13: insufficient difficulty")
|
|
||||||
ErrGenerateTimeout = errors.New("nip13: generating proof of work took too long")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Difficulty counts the number of leading zero bits in an event ID.
|
|
||||||
// It returns a negative number if the event ID is malformed.
|
|
||||||
func Difficulty(eventID string) int {
|
|
||||||
if len(eventID) != 64 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
var zeros int
|
|
||||||
for i := 0; i < 64; i += 2 {
|
|
||||||
if eventID[i:i+2] == "00" {
|
|
||||||
zeros += 8
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var b [1]byte
|
|
||||||
if _, err := hex.Decode(b[:], []byte{eventID[i], eventID[i+1]}); err != nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
zeros += bits.LeadingZeros8(b[0])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return zeros
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate performs proof of work on the specified event until either the target
|
|
||||||
// difficulty is reached or the function runs for longer than the timeout.
|
|
||||||
// The latter case results in ErrGenerateTimeout.
|
|
||||||
//
|
|
||||||
// Upon success, the returned event always contains a "nonce" tag with the target difficulty
|
|
||||||
// commitment, and an updated event.CreatedAt.
|
|
||||||
func Generate(event *nostr.Event, targetDifficulty int, nonceStart int, nonceStep int) (*nostr.Event, error) {
|
|
||||||
nonce := nonceStart
|
|
||||||
tag := nostr.Tag{"nonce", strconv.Itoa(nonceStep), strconv.Itoa(targetDifficulty)}
|
|
||||||
event.Tags = append(event.Tags, tag)
|
|
||||||
|
|
||||||
for {
|
|
||||||
nonce += nonceStep
|
|
||||||
tag[1] = strconv.Itoa(nonce)
|
|
||||||
event.CreatedAt = nostr.Now()
|
|
||||||
if Difficulty(event.GetID()) >= targetDifficulty {
|
|
||||||
return event, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePOW(event *nostr.Event, difficulty int, numCores int) (*nostr.Event, error) {
|
|
||||||
resultChan := make(chan *nostr.Event)
|
|
||||||
errorChan := make(chan error)
|
|
||||||
|
|
||||||
for i := 0; i < numCores; i++ {
|
|
||||||
go func(nonceStart int, nonceStep int) {
|
|
||||||
generatedEvent, err := Generate(event, difficulty, nonceStart, nonceStep)
|
|
||||||
if err != nil {
|
|
||||||
errorChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resultChan <- generatedEvent
|
|
||||||
}(i, numCores)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case result := <-resultChan:
|
|
||||||
return result, nil
|
|
||||||
case err := <-errorChan:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PowRequest struct for the POST request
|
|
||||||
type PowRequest struct {
|
|
||||||
ReqEvent *nostr.Event `json:"req_event"`
|
|
||||||
Difficulty string `json:"difficulty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// handlePOW is the handler function for the "/powgen" endpoint
|
|
||||||
func handlePOW(w http.ResponseWriter, r *http.Request, numCores int) {
|
|
||||||
if r.Method != "POST" {
|
|
||||||
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var powReq PowRequest
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&powReq)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
difficulty, err := strconv.Atoi(powReq.Difficulty)
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate proof of work for the event
|
|
||||||
generatedEvent, err := generatePOW(powReq.ReqEvent, difficulty, numCores)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a response struct
|
|
||||||
type Response struct {
|
|
||||||
Event *nostr.Event `json:"event"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Respond with the generated event and the time taken
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(Response{Event: generatedEvent})
|
|
||||||
}
|
|
||||||
|
|
||||||
// PowRequest struct for the POST request
|
|
||||||
type TestRequest struct {
|
|
||||||
Difficulty string `json:"difficulty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// handlePOW is the handler function for the "/powgen" endpoint
|
|
||||||
func handleTest(w http.ResponseWriter, r *http.Request, numCores int) {
|
|
||||||
if r.Method != "POST" {
|
|
||||||
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var powReq PowRequest
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&powReq)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
difficulty, err := strconv.Atoi(powReq.Difficulty)
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the timer
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
event := &nostr.Event{
|
|
||||||
Kind: nostr.KindTextNote,
|
|
||||||
Content: "It's just me mining my own business",
|
|
||||||
PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243",
|
|
||||||
}
|
|
||||||
pow, err := generatePOW(event, difficulty, numCores)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the duration in milliseconds
|
|
||||||
iterations, _ := strconv.ParseFloat(pow.Tags[0][1], 64)
|
|
||||||
timeTaken := time.Since(start).Seconds()
|
|
||||||
hashrate := iterations / time.Since(start).Seconds()
|
|
||||||
|
|
||||||
// Create a response struct
|
|
||||||
type Response struct {
|
|
||||||
TimeTaken float64 `json:"timeTaken"`
|
|
||||||
Hashrate float64 `json:"hashrate"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Respond with the generated event and the time taken
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(Response{TimeTaken: timeTaken, Hashrate: hashrate})
|
|
||||||
}
|
|
||||||
|
|
||||||
func corsMiddleware(next http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Set headers
|
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
|
||||||
|
|
||||||
// If it's a preflight request, respond with 200
|
|
||||||
if r.Method == "OPTIONS" {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlePOWWithCores(numCores int) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
handlePOW(w, r, numCores)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleTestWithCores(numCores int) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
handleTest(w, r, numCores)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
numCores := runtime.NumCPU()
|
|
||||||
|
|
||||||
http.Handle("/powgen", corsMiddleware(handlePOWWithCores(numCores)))
|
|
||||||
http.Handle("/test", corsMiddleware(handleTestWithCores(numCores)))
|
|
||||||
|
|
||||||
log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil))
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user