Skip to content

Commit

Permalink
feat(server,http): add http services. (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
kehiy authored Sep 27, 2024
1 parent 345d875 commit 000f604
Show file tree
Hide file tree
Showing 20 changed files with 417 additions and 73 deletions.
7 changes: 1 addition & 6 deletions cmd/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,10 @@ func HandleRun(args []string) {
ExitOnError(err)
}

errCh := make(chan error)
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)

go func() {
if err := r.Start(); err != nil {
errCh <- err
}
}()
errCh := r.Start()

select {
case sig := <-sigChan:
Expand Down
82 changes: 75 additions & 7 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import (
"os"

"github.com/dezh-tech/immortal/database"
"github.com/dezh-tech/immortal/server"
"github.com/dezh-tech/immortal/server/http"
"github.com/dezh-tech/immortal/server/websocket"
"github.com/dezh-tech/immortal/types/nip11"
"github.com/joho/godotenv"
"gopkg.in/yaml.v3"
)

// Config reprsents the configs used by relay and other concepts on system.
type Config struct {
Environment string `yaml:"environment"`
ServerConf server.Config `yaml:"server"`
DatabaseConf database.Config `yaml:"database"`
Parameters *Parameters
Environment string `yaml:"environment"`
WebsocketServer websocket.Config `yaml:"ws_server"`
HTTPServer http.Config `yaml:"http_server"`
Database database.Config `yaml:"database"`
Parameters *Parameters
}

// Load loads config from file and env.
Expand All @@ -39,11 +42,13 @@ func Load(path string) (*Config, error) {

if config.Environment != "prod" {
if err := godotenv.Load(); err != nil {
return nil, err
return nil, Error{
reason: err.Error(),
}
}
}

config.DatabaseConf.URI = os.Getenv("IMMO_MONGO_URI")
config.Database.URI = os.Getenv("IMMO_MONGO_URI")

if err = config.basicCheck(); err != nil {
return nil, Error{
Expand All @@ -54,6 +59,69 @@ func Load(path string) (*Config, error) {
return config, nil
}

func (c *Config) GetNIP11Documents() *nip11.RelayInformationDocument {
n11d := &nip11.RelayInformationDocument{
Name: c.Parameters.Name,
Description: c.Parameters.Description,
PubKey: c.Parameters.Pubkey,
SupportedNIPs: c.Parameters.SupportedNips,
Software: c.Parameters.Software,
Version: c.Parameters.Version,
Contact: c.Parameters.Contact,
Limitation: &nip11.RelayLimitationDocument{
MaxMessageLength: c.WebsocketServer.Limitation.MaxMessageLength,
MaxSubscriptions: c.WebsocketServer.Limitation.MaxSubscriptions,
MaxFilters: c.WebsocketServer.Limitation.MaxFilters,
MaxLimit: c.Parameters.Handler.Limitation.MaxLimit,
MaxSubidLength: c.WebsocketServer.Limitation.MaxSubidLength,
MaxEventTags: c.Parameters.Handler.Limitation.MaxEventTags,
MaxContentLength: c.Parameters.Handler.Limitation.MaxContentLength,
MinPowDifficulty: c.WebsocketServer.Limitation.MinPowDifficulty,
AuthRequired: c.WebsocketServer.Limitation.AuthRequired,
PaymentRequired: c.WebsocketServer.Limitation.PaymentRequired,
RestrictedWrites: c.WebsocketServer.Limitation.RestrictedWrites,
},
RelayCountries: c.Parameters.RelayCountries,
LanguageTags: c.Parameters.LanguageTags,
Tags: c.Parameters.Tags,
PostingPolicy: c.Parameters.PostingPolicy,
PaymentsURL: c.Parameters.PaymentsURL,
Icon: c.Parameters.Icon,
}

addmissions := make([]nip11.Admission, 0)
for _, a := range c.Parameters.Fees.Admission {
addmissions = append(addmissions, nip11.Admission{
Amount: a.Amount,
Unit: a.Unit,
})
}

subscription := make([]nip11.Subscription, 0)
for _, s := range c.Parameters.Fees.Subscription {
subscription = append(subscription, nip11.Subscription{
Amount: s.Amount,
Unit: s.Unit,
Period: s.Period,
})
}

publication := make([]nip11.Publication, 0)
for _, p := range c.Parameters.Fees.Publication {
publication = append(publication, nip11.Publication{
Kinds: p.Kinds,
Amount: p.Amount,
Unit: p.Unit,
})
}

n11d.Fees.Admission = addmissions
n11d.Fees.Subscription = subscription
n11d.Fees.Publication = publication

return n11d
}

// basicCheck validates the basic stuff in config.
func (c *Config) basicCheck() error {
return nil
Expand Down
17 changes: 13 additions & 4 deletions config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
# default is prod.
environment: "prod"

# server contains information about websocket server.
server:
# ws_server contains information about websocket server.
ws_server:
# bind is the IP address to be bind and listen on.
# default if local host.
bind: "127.0.0.1"
Expand All @@ -25,13 +25,22 @@ server:
# default is immo_bloom_backup.
bloom_backup_path: "immo_bloom_backup"

# http_server contains information about http server.
http_server:
# bind is the IP address to be bind and listen on.
# default if local host.
bind: "127.0.0.1"
# port is websocket port to be listen on.
# default is 8888.
port: 8888

# database contains details of database connections and limitations.
database:
# db_name is the name of mongodb related to immortal
# db_name is the name of mongodb related to immortal
db_name: "immortal"
# query_timeout_in_ms specifies the maximum duration (in milliseconds) for query execution before timing out.
# default is 3000.
query_timeout_in_ms: 3000
# connection_timeout_in_ms specifies the maximum duration (in milliseconds) that is used for creating connections to the server.
# default is 5000.
connection_timeout_in_ms: 5000
connection_timeout_in_ms: 5000
4 changes: 2 additions & 2 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ func TestLoadfromFile(t *testing.T) {
cfg, err := config.Load("./config.yml")
require.NoError(t, err, "error must be nil.")

assert.Equal(t, uint16(7777), cfg.ServerConf.Port)
assert.Equal(t, "127.0.0.1", cfg.ServerConf.Bind)
assert.Equal(t, uint16(7777), cfg.WebsocketServer.Port)
assert.Equal(t, "127.0.0.1", cfg.WebsocketServer.Bind)
}
77 changes: 51 additions & 26 deletions config/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/dezh-tech/immortal"
"github.com/dezh-tech/immortal/database"
"github.com/dezh-tech/immortal/handler"
"github.com/dezh-tech/immortal/server"
"github.com/dezh-tech/immortal/server/websocket"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
Expand All @@ -18,32 +18,47 @@ type Retention struct {
Kinds interface{} `bson:"kinds,omitempty" json:"kinds,omitempty"`
}

type Fees struct {
type Subscription struct {
Amount int `bson:"amount" json:"amount"`
Unit string `bson:"unit" json:"unit"`
Period int `bson:"period" json:"period"`
}

type Subscription struct {
Subscription []Fees `bson:"subscription" json:"subscription"`
type Admission struct {
Amount int `bson:"amount" json:"amount"`
Unit string `bson:"unit" json:"unit"`
}

type Publication struct {
Kinds []int `bson:"kinds" json:"kinds"`
Amount int `bson:"amount" json:"amount"`
Unit string `bson:"unit" json:"unit"`
}

type Fees struct {
Subscription []Subscription `bson:"subscription,omitempty" json:"subscription,omitempty"`
Publication []Publication `bson:"publication,omitempty" json:"publication,omitempty"`
Admission []Admission `bson:"admission,omitempty" json:"admission,omitempty"`
}

type Parameters struct {
Handler *handler.Config `bson:"handler" json:"handler"`
Server *server.Config `bson:"handler" json:"server"`
Retention *Retention `bson:"retention" json:"retention"`
Fees *Subscription `bson:"fees" json:"fees"`
Name string `bson:"name" json:"name"`
Description string `bson:"description" json:"description"`
Pubkey string `bson:"pubkey" json:"pubkey"`
Software string `bson:"software" json:"software"`
SupportedNips []int `bson:"supported_nips" json:"supported_nips"`
Version string `bson:"version" json:"version"`
RelayCountries []string `bson:"relay_countries" json:"relay_countries"`
LanguageTags []string `bson:"language_tags" json:"language_tags"`
Tags []string `bson:"tags" json:"tags"`
PostingPolicy string `bson:"posting_policy" json:"posting_policy"`
PaymentsURL string `bson:"payments_url" json:"payments_url"`
Icon string `bson:"icon" json:"icon"`
Handler *handler.Config `bson:"handler" json:"handler"`
WebsocketServer *websocket.Config `bson:"server" json:"server"`
Retention *Retention `bson:"retention,omitempty" json:"retention,omitempty"`
Fees *Fees `bson:"fees,omitempty" json:"fees,omitempty"`
Name string `bson:"name" json:"name"`
Description string `bson:"description" json:"description"`
Pubkey string `bson:"pubkey" json:"pubkey"`
Contact string `bson:"contact" json:"contact"`
Software string `bson:"software" json:"software"`
SupportedNips []int `bson:"supported_nips" json:"supported_nips"`
Version string `bson:"version" json:"version"`
RelayCountries []string `bson:"relay_countries,omitempty" json:"relay_countries,omitempty"`
LanguageTags []string `bson:"language_tags,omitempty" json:"language_tags,omitempty"`
Tags []string `bson:"tags,omitempty" json:"tags,omitempty"`
PostingPolicy string `bson:"posting_policy,omitempty" json:"posting_policy,omitempty"`
PaymentsURL string `bson:"payments_url,omitempty" json:"payments_url,omitempty"`
Icon string `bson:"icon,omitempty" json:"icon,omitempty"`
}

func (c *Config) LoadParameters(db *database.Database) error {
Expand All @@ -59,20 +74,20 @@ func (c *Config) LoadParameters(db *database.Database) error {
if errors.Is(err, mongo.ErrNoDocuments) {
// insert default parameters
newDocument := &Parameters{
Name: "Immortal", // relay name
Description: "A Nostr relay for scale", // description
Name: "immortal", // relay name
Description: "a nostr relay designed for scale.", // description
Pubkey: "aca682c51c44c9046461de0cb34bcc6338d5562cdf9062aee9c3ca5a4ca0ab3c", // pubkey
Software: "https://github.com/dezh-tech/immortal", // software repository URL
SupportedNips: []int{1, 11}, // Supported NIPs (protocols)
Version: immortal.StringVersion(), // Version of the relay software
RelayCountries: []string{"US"}, // country support
LanguageTags: []string{"en"}, // language tags
RelayCountries: []string{"*"}, // country support
LanguageTags: []string{"*"}, // language tags
Tags: []string{}, // tags
PostingPolicy: "", // posting policy URL
PaymentsURL: "", // payments URL
Icon: "", // icon URL
Server: &server.Config{
Limitation: &server.Limitation{
WebsocketServer: &websocket.Config{
Limitation: &websocket.Limitation{
MaxMessageLength: 8192, // Maximum length of a single message (in bytes or characters)
MaxSubscriptions: 20, // Maximum number of concurrent subscriptions a client can create
MaxFilters: 20, // Maximum number of filters a client can apply in a subscription
Expand All @@ -93,6 +108,13 @@ func (c *Config) LoadParameters(db *database.Database) error {
CreatedAtUpperLimit: 0, // Latest timestamp allowed for event creation (0 for no limit)
},
},
Retention: &Retention{},
Fees: &Fees{
Subscription: []Subscription{},
Publication: []Publication{},
Admission: []Admission{},
},
Contact: "",
}

insertErr := c.SetParameters(db, newDocument)
Expand All @@ -106,6 +128,7 @@ func (c *Config) LoadParameters(db *database.Database) error {
}

c.Parameters = result
c.WebsocketServer.Limitation = result.WebsocketServer.Limitation

return nil
}
Expand All @@ -119,7 +142,9 @@ func (c *Config) SetParameters(db *database.Database, params *Parameters) error
if insertErr != nil {
return insertErr
}

c.Parameters = params
c.WebsocketServer.Limitation = params.WebsocketServer.Limitation

return nil
}
5 changes: 1 addition & 4 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"time"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
Expand Down Expand Up @@ -36,9 +35,7 @@ func Connect(cfg Config) (*Database, error) {
qCtx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.QueryTimeout)*time.Millisecond)
defer cancel()

var result bson.M
if err := client.Database("admin").RunCommand(qCtx, bson.D{{Key: "ping", Value: 1}}).
Decode(&result); err != nil {
if err := client.Ping(qCtx, nil); err != nil {
return nil, err
}

Expand Down
2 changes: 1 addition & 1 deletion documents/NIPs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The Immortal follows [NIPs](https://github.com/nostr-protocol/nips) and tries to
- [ ] **NIP-02**: Follow List
- [ ] **NIP-03**: OpenTimestamps Attestations for Events
- [ ] **NIP-09**: Event Deletion Request
- [ ] **NIP-11**: Relay Information Document (WIP)
- [X] **NIP-11**: Relay Information Document
- [ ] **NIP-13**: Proof of Work
- [ ] **NIP-15**: Nostr Marketplace (for resilient marketplaces)
- [ ] **NIP-17**: Private Direct Messages
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.5
require (
github.com/bits-and-blooms/bloom/v3 v3.7.0
github.com/btcsuite/btcd/btcec/v2 v2.3.4
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.3
github.com/joho/godotenv v1.5.1
github.com/mailru/easyjson v0.7.7
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
Expand Down
11 changes: 6 additions & 5 deletions handler/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package handler

type Limitation struct {
MaxLimit int `bson:"max_limit" json:"max_limit"`
MaxEventTags int `bson:"max_event_tags" json:"max_event_tags"`
MaxContentLength int `bson:"max_content_length" json:"max_content_length"`
CreatedAtLowerLimit int `bson:"created_at_lower_limit" json:"created_at_lower_limit"`
CreatedAtUpperLimit int `bson:"created_at_upper_limit" json:"created_at_upper_limit"`
MaxLimit uint16 `bson:"max_limit" json:"max_limit"`

Check failure on line 4 in handler/config.go

View workflow job for this annotation

GitHub Actions / lint

tag is not aligned , should be: bson:"max_limit" json:"max_limit" (tagalign)
// todo(@zig)::: move to server.
MaxEventTags uint `bson:"max_event_tags" json:"max_event_tags"`
MaxContentLength uint `bson:"max_content_length" json:"max_content_length"`
CreatedAtLowerLimit uint `bson:"created_at_lower_limit" json:"created_at_lower_limit"`
CreatedAtUpperLimit uint `bson:"created_at_upper_limit" json:"created_at_upper_limit"`
}

type Config struct {
Expand Down
2 changes: 1 addition & 1 deletion handler/req.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (h *Handler) FilterToQuery(fq *filterQuery) (bson.D, *options.FindOptions,
}

// Add Limit to options
if fq.Limit > 0 {
if fq.Limit > 0 && fq.Limit < h.config.Limitation.MaxLimit {
opts.SetLimit(int64(fq.Limit))
} else {
opts.SetLimit(h.config.InitialQueryDefaultLimit)
Expand Down
Loading

0 comments on commit 000f604

Please sign in to comment.