-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from TheRebelOfBabylon/1-refator
Refactor
- Loading branch information
Showing
39 changed files
with
4,617 additions
and
1,094 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,6 @@ | |
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
|
||
# test configs | ||
test.toml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,80 @@ | ||
# tandem | ||
Nostr relay, written in Go | ||
Nostr relay, written in Go. | ||
|
||
# NIP Compliance | ||
- [x] NIP-01 | ||
- [x] NIP-02: Follow List | ||
- [ ] NIP-05: Mapping Nostr keys to DNS-based Internet Identifiers | ||
- [ ] NIP-09: Event Deletion Request | ||
- [ ] NIP-11: Relay Information Document | ||
- [ ] NIP-13: Proof of Work | ||
- [ ] NIP-17: Private Direct Messages | ||
- [ ] NIP-29: Relay-based Groups | ||
- [ ] NIP-40: Expiration Timestamp | ||
- [ ] NIP-42: Authentication of clients to relays | ||
- [ ] NIP-45: Event Counts | ||
- [x] NIP-50: Search Capability* | ||
- [ ] NIP-56: Reporting | ||
- [ ] NIP-64: Chess (Portable Game Notation) | ||
- [x] NIP-65: Relay List Metadata** | ||
- [ ] NIP-70: Protected Events | ||
- [ ] NIP-86: Relay Management API | ||
- [ ] NIP-96: HTTP File Storage Integration | ||
|
||
\* = Search is not ordered by quality or treated differently for each kind. Applies only to content field and no special syntax is used (not even * for wildcard) | ||
|
||
** = tandem does not currently disallow any users from submitting lists | ||
|
||
# Goals | ||
|
||
- Easy to deploy: anyone's Uncle Jim with any sliver of IT knowledge should be able to deploy a relay. | ||
- Easy to moderate: blocking IPs, whitelisting pubkeys, setting up specific moderation rules, all should be achievable without code knowledge. | ||
- Community driven: nostr's main usecase is as a global townsquare but it can also be used to create small communities. tandem's main goal is to serve the latter usecase. | ||
|
||
# Roadmap | ||
|
||
- [ ] Define a roadmap | ||
|
||
# Usage | ||
|
||
## Prerequisites | ||
- [edgedb](https://www.edgedb.com/) | ||
|
||
## Installation | ||
|
||
Define a configuration file: | ||
```toml | ||
[http] | ||
host=localhost # env var: HTTP_HOST | ||
port=5150 # env var: HTTP_PORT | ||
|
||
[log] | ||
level=info # env var: LOG_LEVEL, one of debug|info|error, default: info | ||
log_file_path=/path/to/file.log # env var: LOG_FILE_PATH, optional | ||
|
||
[storage] | ||
uri="edgedb://edgedb:<password>@localhost:10701/main" # env var: STORAGE_URI, replace with your edgedb credentials, one of edgedb|memory | ||
skip_tls_verify=true # env var: STORAGE_SKIP_TLS_VERIFY, default: false | ||
``` | ||
|
||
Run: | ||
```shell | ||
$ tandem -config <path_to_toml_file> | ||
``` | ||
|
||
# Tests | ||
|
||
with edgedb | ||
```shell | ||
$ STORAGE_URI=<your_uri> go test -v -tags=storage,edgedb ./... | ||
``` | ||
|
||
with memorydb | ||
```shell | ||
$ STORAGE_URI="memory://" go test -v -tags=storage,memory ./... | ||
``` | ||
|
||
without storage | ||
```shell | ||
$ go test -v ./... | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,101 +1,79 @@ | ||
package config | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"math" | ||
"path" | ||
"os" | ||
"slices" | ||
|
||
"github.com/BurntSushi/toml" | ||
"github.com/SSSOC-CAN/laniakea/utils" | ||
bg "github.com/SSSOCPaulCote/blunderguard" | ||
"github.com/sethvargo/go-envconfig" | ||
) | ||
|
||
const ( | ||
ErrFileNotFound = bg.Error("file not found") | ||
var ( | ||
validLogLvls = []string{ | ||
"debug", | ||
"info", | ||
"error", | ||
} | ||
defaultLogLvl = "info" | ||
ErrInvalidLogLevel = errors.New("invalid log level") | ||
defaultHost = "localhost" | ||
defaultPort = 5000 | ||
) | ||
|
||
type Info struct { | ||
URL string `toml:"url"` // the advertised URL for websocket connection Ex: wss://nostr.example.com | ||
Name string `toml:"name"` // name for your relay | ||
Description string `toml:"description"` // description for clients | ||
Pubkey string `toml:"pubkey"` // Relay admin contact pubkey | ||
Contact string `toml:"contact"` // Relay admin contact URI | ||
} | ||
|
||
type Logging struct { | ||
LogLevel string `toml:"log_level"` // (TRACE|DEBUG|INFO|ERROR) | ||
ConsoleOutput bool `toml:"console_output"` // Print logs to console | ||
LogFileDir string `toml:"log_file_location"` // location of the log files. Default is AppData/Tandem | Application Support/Tandem or ~/.tandem | ||
LogFileSize uint32 `toml:"log_file_size"` // Maximum size of a log file in MB | ||
MaxLogFiles uint16 `toml:"max_log_files"` // Maximum number of log files in a rotation. 0 for no rotation: Default 0 | ||
} | ||
|
||
type Data struct { | ||
DataDir string `toml:"data_directory"` // Directory for the database. Defaults to AppData | Application Support | ~/.tandem | ||
} | ||
|
||
type Network struct { | ||
BindAddress string `toml:"bind_address"` // Bind to this network address | ||
Port uint16 `toml:"port"` // Port to listen on | ||
PingInterval uint16 `toml:"ping_interval"` // WebSockets ping interval in seconds, default: 300 | ||
type HTTP struct { | ||
Host string `toml:"host" env:"HOST, overwrite"` | ||
Port int `toml:"port" env:"PORT, overwrite"` | ||
} | ||
|
||
type Limits struct { | ||
MsgPerSec uint32 `toml:"message_per_second"` // Limit of events that can be created per second | ||
MaxEventSize uint32 `toml:"maximum_event_size"` // Maximum size in bytes, an event can be. Max=2^32 - 1 | ||
MaxWSMsgSize uint32 `toml:"maximum_ws_message_size"` // Maximum size in bytes a websocket message can be. Max=2^32 - 1 | ||
RejectFutureSecs uint64 `toml:"reject_future_seconds"` // Reject events that have timestamps greater than this many seconds in the future. Recommended to reject anything greater than 30 minutes from the current time, but the default is to allow any date. | ||
type Log struct { | ||
Level string `toml:"level" env:"LEVEL, overwrite"` | ||
LogFilePath string `toml:"log_file_path" env:"FILE_PATH, overwrite"` | ||
} | ||
|
||
type Authorization struct { | ||
PubkeyWhitelist []string `toml:"whitelist"` // List of authorized pubkeys for event publishing. If not set, all pubkeys are accepted | ||
type Storage struct { | ||
Uri string `toml:"uri" env:"URI, overwrite"` | ||
SkipTlsVerify bool `toml:"skip_tls_verify" env:"SKIP_TLS_VERIFY, overwrite"` | ||
} | ||
|
||
type Config struct { | ||
Relay Info // relevant relay information | ||
Logging Logging // relevant logging settings | ||
Database Data // relevant data settings | ||
Network Network // relevant network settings | ||
Limits Limits // relevant limit settings | ||
Auth Authorization // relevant auth settings | ||
HTTP HTTP `toml:"http" env:", prefix=HTTP_"` | ||
Log Log `toml:"log" env:", prefix=LOG_"` | ||
Storage Storage `toml:"storage" env:", prefix=STORAGE_"` | ||
} | ||
|
||
var ( | ||
config_file_name = "tandem.toml" | ||
default_bind_address = "0.0.0.0" | ||
default_port uint16 = 5150 | ||
default_ping_interval uint16 = 300 | ||
default_data_dir = utils.AppDataDir("tandem", false) | ||
default_log_level = "ERROR" | ||
default_log_dir = default_data_dir | ||
default_console_out = true | ||
default_msg_per_sec uint32 = 50000 | ||
default_max_event_size uint32 = math.MaxUint32 | ||
default_max_ws_msg_size uint32 = math.MaxUint32 | ||
default_reject_future_secs uint64 = 1800 | ||
default_log_size uint32 = 10 | ||
default_max_log_files uint16 = 0 | ||
default_config = func() Config { | ||
return Config{ | ||
Logging: Logging{LogLevel: default_log_level, LogFileDir: default_log_dir, ConsoleOutput: default_console_out, LogFileSize: default_log_size, MaxLogFiles: default_max_log_files}, | ||
Database: Data{DataDir: default_data_dir}, | ||
Network: Network{BindAddress: default_bind_address, Port: default_port, PingInterval: default_ping_interval}, | ||
Limits: Limits{MsgPerSec: default_msg_per_sec, MaxEventSize: default_max_event_size, MaxWSMsgSize: default_max_ws_msg_size, RejectFutureSecs: default_reject_future_secs}, | ||
} | ||
// ReadConfig reads the given config file | ||
func ReadConfig(pathToConfig string) (*Config, error) { | ||
var cfg Config | ||
cfgFileBytes, err := os.ReadFile(pathToConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
) | ||
if err := toml.Unmarshal(cfgFileBytes, &cfg); err != nil { | ||
return nil, err | ||
} | ||
// env vars override anything in the config file | ||
if err := envconfig.Process(context.Background(), &cfg); err != nil { | ||
return nil, err | ||
} | ||
return &cfg, nil | ||
} | ||
|
||
// Initialize the config either from a configuration file or using default values | ||
func InitConfig(cfgDir string) (*Config, error) { | ||
cfgFile := path.Join(cfgDir, config_file_name) | ||
config := default_config() | ||
if utils.FileExists(cfgFile) { | ||
_, err := toml.DecodeFile(cfgFile, &config) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} else { | ||
fmt.Println("Using default configuration...") | ||
// Validate will perform validation on the given configuration | ||
func (c *Config) Validate() error { | ||
if c.Log.Level == "" { | ||
c.Log.Level = defaultLogLvl | ||
} | ||
if !slices.Contains(validLogLvls, c.Log.Level) { | ||
return fmt.Errorf("%w: %s", ErrInvalidLogLevel, c.Log.Level) | ||
} | ||
if c.HTTP.Host == "" { | ||
c.HTTP.Host = defaultHost | ||
} | ||
if c.HTTP.Port == 0 { | ||
c.HTTP.Port = defaultPort | ||
} | ||
return &config, nil | ||
return nil | ||
} |
Oops, something went wrong.