diff --git a/.gitignore b/.gitignore index cad2309..3b20866 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/tmp \ No newline at end of file +/tmp +config.yml \ No newline at end of file diff --git a/config.example.yml b/config.example.yml index e69de29..a3bf0cd 100644 --- a/config.example.yml +++ b/config.example.yml @@ -0,0 +1,8 @@ +mongodb: + uri: "mongodb://localhost:27017/" + database: "grain" +collections: + event_kind0: "event-kind0" + event_kind1: "event-kind1" +server: + address: ":8080" diff --git a/db/db.go b/db/db.go new file mode 100644 index 0000000..d5e2939 --- /dev/null +++ b/db/db.go @@ -0,0 +1,33 @@ +package db + +import ( + "context" + "fmt" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func InitDB(uri, database string) (*mongo.Client, error) { + clientOptions := options.Client().ApplyURI(uri) + client, err := mongo.Connect(context.TODO(), clientOptions) + if err != nil { + return nil, err + } + + // Check the connection + err = client.Ping(context.TODO(), nil) + if err != nil { + return nil, err + } + fmt.Println("Connected to MongoDB!") + + return client, nil +} + +func DisconnectDB(client *mongo.Client) { + if err := client.Disconnect(context.TODO()); err != nil { + fmt.Println("Error disconnecting from MongoDB:", err) + } + fmt.Println("Disconnected from MongoDB!") +} diff --git a/events/events.go b/events/events.go new file mode 100644 index 0000000..1d6b408 --- /dev/null +++ b/events/events.go @@ -0,0 +1,70 @@ +package events + +import ( + "context" + "fmt" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type Event struct { + CreatedAt int64 `json:"created_at"` + Kind int `json:"kind"` + Content string `json:"content"` + Tags []string `json:"tags"` + PubKey string `json:"pubkey"` + ID string `json:"id"` + Sig string `json:"sig"` +} + +var eventKind0Collection *mongo.Collection +var eventKind1Collection *mongo.Collection + +func InitCollections(client *mongo.Client, eventKind0, eventKind1 string) { + eventKind0Collection = client.Database("grain").Collection(eventKind0) + eventKind1Collection = client.Database("grain").Collection(eventKind1) + + indexModel := mongo.IndexModel{ + Keys: bson.D{{Key: "id", Value: 1}}, + Options: options.Index().SetUnique(true), + } + _, err := eventKind0Collection.Indexes().CreateOne(context.TODO(), indexModel) + if err != nil { + fmt.Println("Failed to create index on event-kind0: ", err) + } + _, err = eventKind1Collection.Indexes().CreateOne(context.TODO(), indexModel) + if err != nil { + fmt.Println("Failed to create index on event-kind1: ", err) + } +} + +func HandleEvent(ctx context.Context, evt Event) error { + var collection *mongo.Collection + switch evt.Kind { + case 0: + collection = eventKind0Collection + case 1: + return HandleEventKind1(ctx, evt, eventKind1Collection) + default: + fmt.Println("Unknown event kind:", evt.Kind) + return fmt.Errorf("unknown event kind: %d", evt.Kind) + } + + _, err := collection.InsertOne(ctx, evt) + if err != nil { + fmt.Println("Error inserting event into MongoDB:", err) + return err + } + + fmt.Println("Inserted event into MongoDB:", evt.ID) + return nil +} + +func GetCollections() map[string]*mongo.Collection { + return map[string]*mongo.Collection{ + "eventKind0": eventKind0Collection, + "eventKind1": eventKind1Collection, + } +} diff --git a/events/kind1.go b/events/kind1.go new file mode 100644 index 0000000..331f209 --- /dev/null +++ b/events/kind1.go @@ -0,0 +1,33 @@ +package events + +import ( + "context" + "fmt" + + "go.mongodb.org/mongo-driver/mongo" +) + +func HandleEventKind1(ctx context.Context, evt Event, collection *mongo.Collection) error { + // Perform specific validation for event kind 1 + if !isValidEventKind1(evt) { + return fmt.Errorf("validation failed for event kind 1: %s", evt.ID) + } + + // Insert event into MongoDB + _, err := collection.InsertOne(ctx, evt) + if err != nil { + fmt.Println("Error inserting event into MongoDB:", err) + return err + } + + fmt.Println("Inserted event kind 1 into MongoDB:", evt.ID) + return nil +} + +func isValidEventKind1(evt Event) bool { + // Placeholder for actual validation logic + if evt.Content == "" { + return false + } + return true +} diff --git a/go.mod b/go.mod index 12e041f..d7d33d7 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module GRAIN +module grain go 1.22.2 @@ -18,4 +18,5 @@ require ( golang.org/x/crypto v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/text v0.16.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 34e10b9..9ecfdb3 100644 --- a/go.sum +++ b/go.sum @@ -50,3 +50,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go index 06f986f..63a3d31 100644 --- a/main.go +++ b/main.go @@ -1,150 +1,43 @@ package main import ( - "context" - "encoding/json" "fmt" "log" "net/http" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" + "grain/db" + "grain/events" + "grain/requests" + "grain/utils" + "golang.org/x/net/websocket" ) -// Event represents the structure of the incoming events -type Event struct { - CreatedAt int64 `json:"created_at"` - Kind int `json:"kind"` - Content string `json:"content"` - Tags []string `json:"tags"` - PubKey string `json:"pubkey"` - ID string `json:"id"` - Sig string `json:"sig"` -} - -// Database client and collections -var client *mongo.Client -var eventKind0Collection *mongo.Collection -var eventKind1Collection *mongo.Collection - -func handler(ws *websocket.Conn) { - var msg string - for { - err := websocket.Message.Receive(ws, &msg) - if err != nil { - fmt.Println("Error receiving message:", err) - return - } - fmt.Println("Received message:", msg) - - // Parse the received message - var event []interface{} - err = json.Unmarshal([]byte(msg), &event) - if err != nil { - fmt.Println("Error parsing message:", err) - return - } - - if len(event) < 2 || event[0] != "EVENT" { - fmt.Println("Invalid event format") - continue - } - - // Convert the event map to an Event struct - eventData, ok := event[1].(map[string]interface{}) - if !ok { - fmt.Println("Invalid event data format") - continue - } - eventBytes, err := json.Marshal(eventData) - if err != nil { - fmt.Println("Error marshaling event data:", err) - continue - } - - var evt Event - err = json.Unmarshal(eventBytes, &evt) - if err != nil { - fmt.Println("Error unmarshaling event data:", err) - continue - } - - // Store the event in the appropriate MongoDB collection - var collection *mongo.Collection - switch evt.Kind { - case 0: - collection = eventKind0Collection - case 1: - collection = eventKind1Collection - default: - fmt.Println("Unknown event kind:", evt.Kind) - continue - } - - _, err = collection.InsertOne(context.TODO(), evt) - if err != nil { - fmt.Println("Error inserting event into MongoDB:", err) - continue - } - - fmt.Println("Inserted event into MongoDB:", evt.ID) - - err = websocket.Message.Send(ws, "Echo: "+msg) - if err != nil { - fmt.Println("Error sending message:", err) - return - } - } -} - func main() { - // Initialize MongoDB client - var err error - clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/") - client, err = mongo.Connect(context.TODO(), clientOptions) + // Load configuration + config, err := utils.LoadConfig("config.yml") if err != nil { - log.Fatal(err) + log.Fatal("Error loading config: ", err) } - // Check the connection - err = client.Ping(context.TODO(), nil) + // Initialize MongoDB client + client, err := db.InitDB(config.MongoDB.URI, config.MongoDB.Database) if err != nil { - log.Fatal(err) + log.Fatal("Error initializing database: ", err) } - fmt.Println("Connected to MongoDB!") + defer db.DisconnectDB(client) // Initialize collections - eventKind0Collection = client.Database("grain").Collection("event-kind0") - eventKind1Collection = client.Database("grain").Collection("event-kind1") + events.InitCollections(client, config.Collections.EventKind0, config.Collections.EventKind1) - // Ensure collections exist by creating an index (which will implicitly create the collections) - indexModel := mongo.IndexModel{ - Keys: bson.D{{Key: "id", Value: 1}}, - Options: options.Index().SetUnique(true), - } - _, err = eventKind0Collection.Indexes().CreateOne(context.TODO(), indexModel) - if err != nil { - log.Fatal("Failed to create index on event-kind0: ", err) - } - _, err = eventKind1Collection.Indexes().CreateOne(context.TODO(), indexModel) - if err != nil { - log.Fatal("Failed to create index on event-kind1: ", err) - } + // Set the MongoDB collections in the requests package + requests.SetCollections(events.GetCollections()) // Start WebSocket server - http.Handle("/", websocket.Handler(handler)) - fmt.Println("WebSocket server started on :8080") - err = http.ListenAndServe(":8080", nil) + http.Handle("/", websocket.Handler(requests.Handler)) + fmt.Println("WebSocket server started on", config.Server.Address) + err = http.ListenAndServe(config.Server.Address, nil) if err != nil { fmt.Println("Error starting server:", err) } - - // Disconnect MongoDB client on exit - defer func() { - if err = client.Disconnect(context.TODO()); err != nil { - log.Fatal(err) - } - }() } diff --git a/requests/requests.go b/requests/requests.go new file mode 100644 index 0000000..1a410d0 --- /dev/null +++ b/requests/requests.go @@ -0,0 +1,87 @@ +package requests + +import ( + "context" + "encoding/json" + "fmt" + + "grain/events" + + "go.mongodb.org/mongo-driver/mongo" + "golang.org/x/net/websocket" +) + +var eventKind0Collection, eventKind1Collection *mongo.Collection + +func SetCollections(collections map[string]*mongo.Collection) { + eventKind0Collection = collections["eventKind0"] + eventKind1Collection = collections["eventKind1"] +} + +func Handler(ws *websocket.Conn) { + var msg string + for { + err := websocket.Message.Receive(ws, &msg) + if err != nil { + fmt.Println("Error receiving message:", err) + return + } + fmt.Println("Received message:", msg) + + // Parse the received message + var event []interface{} + err = json.Unmarshal([]byte(msg), &event) + if err != nil { + fmt.Println("Error parsing message:", err) + return + } + + if len(event) < 2 || event[0] != "EVENT" { + fmt.Println("Invalid event format") + continue + } + + // Convert the event map to an Event struct + eventData, ok := event[1].(map[string]interface{}) + if !ok { + fmt.Println("Invalid event data format") + continue + } + eventBytes, err := json.Marshal(eventData) + if err != nil { + fmt.Println("Error marshaling event data:", err) + continue + } + + var evt events.Event + err = json.Unmarshal(eventBytes, &evt) + if err != nil { + fmt.Println("Error unmarshaling event data:", err) + continue + } + + // Store the event in the appropriate MongoDB collection + //var collection *mongo.Collection + //switch evt.Kind { + //case 0: + // collection = eventKind0Collection + //case 1: + // collection = eventKind1Collection + //default: + // fmt.Println("Unknown event kind:", evt.Kind) + // continue + //} + + err = events.HandleEvent(context.TODO(), evt) + if err != nil { + fmt.Println("Error handling event:", err) + continue + } + + err = websocket.Message.Send(ws, "Echo: "+msg) + if err != nil { + fmt.Println("Error sending message:", err) + return + } + } +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..02219b6 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,36 @@ +package utils + +import ( + "io/ioutil" + + "gopkg.in/yaml.v2" +) + +type Config struct { + MongoDB struct { + URI string `yaml:"uri"` + Database string `yaml:"database"` + } `yaml:"mongodb"` + Collections struct { + EventKind0 string `yaml:"event_kind0"` + EventKind1 string `yaml:"event_kind1"` + } `yaml:"collections"` + Server struct { + Address string `yaml:"address"` + } `yaml:"server"` +} + +func LoadConfig(filename string) (*Config, error) { + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + return nil, err + } + + return &config, nil +}