mirror of
https://github.com/0ceanSlim/grain.git
synced 2024-11-23 17:07:13 +00:00
Compare commits
No commits in common. "7edbee13ac9dc7b8589d6fbb604d3b45f61a151c" and "a2f7f0a5b4c5f81d7c053bcd26b53327324df491" have entirely different histories.
7edbee13ac
...
a2f7f0a5b4
@ -40,19 +40,12 @@ func ImportEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
var events []map[string]interface{}
|
var events []map[string]interface{}
|
||||||
var lastEventCreatedAt int64 = 0 // Track the timestamp of the last event fetched
|
events, err = fetchEventsFromRelay(pubkey, url)
|
||||||
|
|
||||||
for {
|
|
||||||
events, err = fetchEventsFromRelay(pubkey, url, lastEventCreatedAt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChan <- fmt.Errorf("error fetching events from relay %s: %w", url, err)
|
errorChan <- fmt.Errorf("error fetching events from relay %s: %w", url, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(events) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
err = sendEventsToRelay(events)
|
err = sendEventsToRelay(events)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChan <- fmt.Errorf("error sending events to relay: %w", err)
|
errorChan <- fmt.Errorf("error sending events to relay: %w", err)
|
||||||
@ -60,10 +53,6 @@ func ImportEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
totalEvents += len(events)
|
totalEvents += len(events)
|
||||||
|
|
||||||
// Update lastEventCreatedAt with the timestamp of the last event fetched
|
|
||||||
lastEventCreatedAt = int64(events[len(events)-1]["created_at"].(float64))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totalEventsChan <- totalEvents
|
totalEventsChan <- totalEvents
|
||||||
@ -74,7 +63,7 @@ func ImportEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
renderResult(w, true, "Events imported successfully", totalEvents)
|
renderResult(w, true, "Events imported successfully", totalEvents)
|
||||||
case err := <-errorChan:
|
case err := <-errorChan:
|
||||||
renderResult(w, false, err.Error(), 0)
|
renderResult(w, false, err.Error(), 0)
|
||||||
case <-time.After(10 * time.Minute): // Increase timeout for large imports
|
case <-time.After(5 * time.Minute):
|
||||||
renderResult(w, false, "Timeout importing events", 0)
|
renderResult(w, false, "Timeout importing events", 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +92,7 @@ func renderResult(w http.ResponseWriter, success bool, message string, count int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchEventsFromRelay(pubkey, relayUrl string, lastEventCreatedAt int64) ([]map[string]interface{}, error) {
|
func fetchEventsFromRelay(pubkey, relayUrl string) ([]map[string]interface{}, error) {
|
||||||
log.Printf("Connecting to relay: %s", relayUrl)
|
log.Printf("Connecting to relay: %s", relayUrl)
|
||||||
conn, err := websocket.Dial(relayUrl, "", "http://localhost/")
|
conn, err := websocket.Dial(relayUrl, "", "http://localhost/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -113,17 +102,7 @@ func fetchEventsFromRelay(pubkey, relayUrl string, lastEventCreatedAt int64) ([]
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
log.Printf("Connected to relay: %s", relayUrl)
|
log.Printf("Connected to relay: %s", relayUrl)
|
||||||
|
|
||||||
filters := map[string]interface{}{
|
reqMessage := fmt.Sprintf(`["REQ", "import-sub", {"authors": ["%s"]}]`, pubkey)
|
||||||
"authors": []string{pubkey},
|
|
||||||
"limit": 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastEventCreatedAt > 0 {
|
|
||||||
filters["until"] = lastEventCreatedAt - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
filtersJSON, _ := json.Marshal(filters)
|
|
||||||
reqMessage := fmt.Sprintf(`["REQ", "import-sub", %s]`, filtersJSON)
|
|
||||||
log.Printf("Sending request: %s", reqMessage)
|
log.Printf("Sending request: %s", reqMessage)
|
||||||
if _, err := conn.Write([]byte(reqMessage)); err != nil {
|
if _, err := conn.Write([]byte(reqMessage)); err != nil {
|
||||||
log.Printf("Error sending request to relay %s: %v", relayUrl, err)
|
log.Printf("Error sending request to relay %s: %v", relayUrl, err)
|
||||||
@ -177,7 +156,7 @@ func sendEventsToRelay(events []map[string]interface{}) error {
|
|||||||
|
|
||||||
relayUrl := fmt.Sprintf("ws://localhost%s", cfg.Server.Port)
|
relayUrl := fmt.Sprintf("ws://localhost%s", cfg.Server.Port)
|
||||||
|
|
||||||
batchSize := 20 // Reduce the batch size to avoid connection issues
|
batchSize := 5 // Reduce the batch size to avoid connection issues
|
||||||
for i := 0; i < len(events); i += batchSize {
|
for i := 0; i < len(events); i += batchSize {
|
||||||
end := i + batchSize
|
end := i + batchSize
|
||||||
if end > len(events) {
|
if end > len(events) {
|
||||||
@ -188,9 +167,6 @@ func sendEventsToRelay(events []map[string]interface{}) error {
|
|||||||
if err := sendBatchToRelay(batch, relayUrl); err != nil {
|
if err := sendBatchToRelay(batch, relayUrl); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a short period to avoid overloading the relay server
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -220,8 +196,9 @@ func sendBatchToRelay(events []map[string]interface{}, relayUrl string) error {
|
|||||||
}
|
}
|
||||||
log.Printf("Sent event to local relay: %s", event["id"])
|
log.Printf("Sent event to local relay: %s", event["id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a short period to avoid overloading the relay server
|
// Wait for a short period to avoid overloading the relay server
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ func RootHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := PageData{
|
data := PageData{
|
||||||
Title: "GRAIN Dashboard",
|
Title: "GRAIN Relay",
|
||||||
Events: events,
|
Events: events,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{{define "view"}}
|
{{define "view"}}
|
||||||
<main class="flex flex-col items-center justify-center p-8">
|
<main class="flex flex-col items-center justify-center p-8">
|
||||||
<div class="mb-4">{{.Title}}</div>
|
<div class="mb-4">You are now viewing the {{.Title}}</div>
|
||||||
<div
|
<div
|
||||||
class="container flex justify-center p-4 border-2 border-gray-600 border-solid rounded-md w-fit"
|
class="container flex justify-center p-4 border-2 border-gray-600 border-solid rounded-md w-fit"
|
||||||
>
|
>
|
||||||
@ -10,7 +10,6 @@
|
|||||||
hx-target="#result"
|
hx-target="#result"
|
||||||
hx-indicator="#spinner"
|
hx-indicator="#spinner"
|
||||||
>
|
>
|
||||||
<div class="content-right">
|
|
||||||
<div>
|
<div>
|
||||||
<label for="pubkey">Pubkey:</label>
|
<label for="pubkey">Pubkey:</label>
|
||||||
<input
|
<input
|
||||||
@ -32,25 +31,15 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
class="p-2 m-2 font-bold bg-green-500 rounded-md font-xl"
|
class="p-2 m-2 font-bold bg-green-500 rounded-md font-xl"
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Import Events
|
Import Events
|
||||||
</button>
|
</button>
|
||||||
<div class="font-bold text-md">
|
|
||||||
⚠️ This Feature is Experimental<br />
|
|
||||||
If you are whitelisted, this SHOULD capture all of your events<br />
|
|
||||||
Please Be Patient. Imports can take quite some time due to Rate Limits
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div id="spinner" class="spinner"></div>
|
||||||
id="spinner"
|
|
||||||
class="m-4 text-lg font-bold text-center spinner"
|
|
||||||
style="display: none"
|
|
||||||
></div>
|
|
||||||
<div id="result" class="p-2 m-2 text-xl font-bold"></div>
|
<div id="result" class="p-2 m-2 text-xl font-bold"></div>
|
||||||
<button
|
<button
|
||||||
hx-get="/"
|
hx-get="/"
|
||||||
@ -67,11 +56,9 @@
|
|||||||
document.getElementById("spinner").style.display = "block";
|
document.getElementById("spinner").style.display = "block";
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("htmx:afterRequest", function () {
|
document
|
||||||
document.getElementById("spinner").style.display = "none";
|
.getElementById("result")
|
||||||
});
|
.addEventListener("htmx:afterRequest", function () {
|
||||||
|
|
||||||
document.addEventListener("htmx:requestError", function () {
|
|
||||||
document.getElementById("spinner").style.display = "none";
|
document.getElementById("spinner").style.display = "none";
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<button
|
<button
|
||||||
hx-get="/import-events"
|
hx-get="/import-events"
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
class="p-2 mb-2 text-white bg-purple-500 rounded-md"
|
class="p-2 mb-2 text-white bg-orange-400 rounded-md"
|
||||||
hx-target="body"
|
hx-target="body"
|
||||||
>
|
>
|
||||||
Import Events
|
Import Events
|
||||||
|
@ -1,23 +1,5 @@
|
|||||||
{{define "footer"}}
|
{{define "footer"}}
|
||||||
<footer class="text-textMuted">
|
<footer class="text-textMuted">
|
||||||
<p>
|
<p>© 2024 GRAIN 🌾, made with 💜 by OceanSlim</p>
|
||||||
© 2024
|
|
||||||
<a href="https://github.com/0ceanSlim/grain" class="text-purple-400"
|
|
||||||
>GRAIN 🌾</a
|
|
||||||
>, made with 💜 by
|
|
||||||
<a
|
|
||||||
href="https://njump.me/npub1zmc6qyqdfnllhnzzxr5wpepfpnzcf8q6m3jdveflmgruqvd3qa9sjv7f60"
|
|
||||||
class="text-purple-400"
|
|
||||||
>OceanSlim</a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
GRAIN is Proudly Open Source. Don't hesitate to
|
|
||||||
<a
|
|
||||||
href="https://github.com/0ceanSlim/grain?tab=readme-ov-file#development"
|
|
||||||
class="text-purple-400"
|
|
||||||
>contribute</a
|
|
||||||
>!
|
|
||||||
</p>
|
|
||||||
</footer>
|
</footer>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -29,10 +29,10 @@
|
|||||||
<style>
|
<style>
|
||||||
.spinner {
|
.spinner {
|
||||||
display: none;
|
display: none;
|
||||||
width: 32px;
|
width: 50px;
|
||||||
height: 32px;
|
height: 50px;
|
||||||
border: 5px solid purple;
|
border: 5px solid lightgray;
|
||||||
border-top: 5px solid violet;
|
border-top: 5px solid blue;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 1s linear infinite;
|
animation: spin 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"grain/server/handlers/kinds"
|
"grain/server/handlers/kinds"
|
||||||
"grain/server/handlers/response"
|
"grain/server/handlers/response"
|
||||||
"grain/server/utils"
|
"grain/server/utils"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
relay "grain/server/types"
|
relay "grain/server/types"
|
||||||
|
|
||||||
@ -73,13 +74,13 @@ func HandleKind(ctx context.Context, evt relay.Event, ws *websocket.Conn, eventS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the kind is whitelisted
|
// Check if the kind is whitelisted
|
||||||
if config.GetConfig().KindWhitelist.Enabled && !utils.IsKindWhitelisted(evt.Kind) {
|
if config.GetConfig().KindWhitelist.Enabled && !isKindWhitelisted(evt.Kind) {
|
||||||
response.SendOK(ws, evt.ID, false, "not allowed: event kind is not whitelisted")
|
response.SendOK(ws, evt.ID, false, "not allowed: event kind is not whitelisted")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check pubkey/npub whitelist only if the kind is not whitelisted
|
// Check pubkey/npub whitelist only if the kind is not whitelisted
|
||||||
if config.GetConfig().PubkeyWhitelist.Enabled && !utils.IsPubKeyWhitelisted(evt.PubKey) {
|
if config.GetConfig().PubkeyWhitelist.Enabled && !isPubKeyWhitelisted(evt.PubKey) {
|
||||||
response.SendOK(ws, evt.ID, false, "not allowed: pubkey or npub is not whitelisted")
|
response.SendOK(ws, evt.ID, false, "not allowed: pubkey or npub is not whitelisted")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -147,3 +148,52 @@ func determineCategory(kind int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to check if a pubkey or npub is whitelisted
|
||||||
|
func isPubKeyWhitelisted(pubKey string) bool {
|
||||||
|
cfg := config.GetConfig()
|
||||||
|
if !cfg.PubkeyWhitelist.Enabled {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check pubkeys
|
||||||
|
for _, whitelistedKey := range cfg.PubkeyWhitelist.Pubkeys {
|
||||||
|
if pubKey == whitelistedKey {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check npubs
|
||||||
|
for _, npub := range cfg.PubkeyWhitelist.Npubs {
|
||||||
|
decodedPubKey, err := utils.DecodeNpub(npub)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error decoding npub:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if pubKey == decodedPubKey {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isKindWhitelisted(kind int) bool {
|
||||||
|
cfg := config.GetConfig()
|
||||||
|
if !cfg.KindWhitelist.Enabled {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check event kinds
|
||||||
|
for _, whitelistedKindStr := range cfg.KindWhitelist.Kinds {
|
||||||
|
whitelistedKind, err := strconv.Atoi(whitelistedKindStr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error converting whitelisted kind to int:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if kind == whitelistedKind {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"grain/config"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Helper function to check if a pubkey or npub is whitelisted
|
|
||||||
func IsPubKeyWhitelisted(pubKey string) bool {
|
|
||||||
cfg := config.GetConfig()
|
|
||||||
if !cfg.PubkeyWhitelist.Enabled {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check pubkeys
|
|
||||||
for _, whitelistedKey := range cfg.PubkeyWhitelist.Pubkeys {
|
|
||||||
if pubKey == whitelistedKey {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check npubs
|
|
||||||
for _, npub := range cfg.PubkeyWhitelist.Npubs {
|
|
||||||
decodedPubKey, err := DecodeNpub(npub)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error decoding npub:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if pubKey == decodedPubKey {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsKindWhitelisted(kind int) bool {
|
|
||||||
cfg := config.GetConfig()
|
|
||||||
if !cfg.KindWhitelist.Enabled {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check event kinds
|
|
||||||
for _, whitelistedKindStr := range cfg.KindWhitelist.Kinds {
|
|
||||||
whitelistedKind, err := strconv.Atoi(whitelistedKindStr)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error converting whitelisted kind to int:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if kind == whitelistedKind {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user