package zooid import ( "fiatjaf.com/nostr" "fmt" "github.com/BurntSushi/toml" "os" "path/filepath" "slices" ) type Role struct { Pubkeys []string `toml:"pubkeys" json:"pubkeys"` CanInvite bool `toml:"can_invite" json:"can_invite"` CanManage bool `toml:"can_manage" json:"can_manage"` } type Config struct { Host string `toml:"host" json:"host"` Schema string `toml:"schema" json:"schema"` Secret string `toml:"secret" json:"secret"` Inactive bool `toml:"inactive" json:"inactive"` Info struct { Name string `toml:"name" json:"name"` Icon string `toml:"icon" json:"icon"` Pubkey string `toml:"pubkey" json:"pubkey"` Description string `toml:"description" json:"description"` } `toml:"info" json:"info"` Policy struct { PublicJoin bool `toml:"public_join" json:"public_join"` StripSignatures bool `toml:"strip_signatures" json:"strip_signatures"` } `toml:"policy" json:"policy"` Groups struct { Enabled bool `toml:"enabled" json:"enabled"` } `toml:"groups" json:"groups"` Push struct { Enabled bool `toml:"enabled" json:"enabled"` } `toml:"push" json:"push"` Management struct { Enabled bool `toml:"enabled" json:"enabled"` } `toml:"management" json:"management"` Blossom struct { Enabled bool `toml:"enabled" json:"enabled"` } `toml:"blossom" json:"blossom"` Livekit struct { ServerURL string `toml:"server_url" json:"server_url"` APIKey string `toml:"api_key" json:"api_key"` APISecret string `toml:"api_secret" json:"api_secret"` } `toml:"livekit" json:"livekit"` Roles map[string]Role `toml:"roles" json:"roles"` // Private/parsed values path string secret nostr.SecretKey } func LoadConfig(filename string) (*Config, error) { path := filepath.Join(Env("CONFIG"), filename) return LoadConfigFromPath(path) } func LoadConfigFromPath(path string) (*Config, error) { var config Config if _, err := toml.DecodeFile(path, &config); err != nil { return nil, fmt.Errorf("Failed to parse config file %s: %w", path, err) } if config.Host == "" { return nil, fmt.Errorf("host is required") } if config.Schema == "" { return nil, fmt.Errorf("schema is required") } if config.Info.Pubkey == "" { return nil, fmt.Errorf("info.pubkey is required") } secret, err := nostr.SecretKeyFromHex(config.Secret) if err != nil { return nil, err } // Save the path for later config.path = path // Make the secret... secret config.Secret = "" config.secret = secret return &config, nil } func (config *Config) Save() error { // Restore the secret key to the public field for saving config.Secret = config.secret.Hex() file, err := os.Create(config.path) if err != nil { return fmt.Errorf("Failed to open config file %s: %w", config.path, err) } defer file.Close() encoder := toml.NewEncoder(file) if err := encoder.Encode(config); err != nil { return fmt.Errorf("Failed to encode config file %s: %w", config.path, err) } // Clear the secret again config.Secret = "" return nil } func (config *Config) SetName(name string) error { config.Info.Name = name return config.Save() } func (config *Config) SetDescription(description string) error { config.Info.Description = description return config.Save() } func (config *Config) SetIcon(icon string) error { config.Info.Icon = icon return config.Save() } func (config *Config) Sign(event *nostr.Event) error { return event.Sign(config.secret) } func (config *Config) GetSelf() nostr.PubKey { return config.secret.Public() } func (config *Config) IsSelf(pubkey nostr.PubKey) bool { return pubkey == config.GetSelf() } func (config *Config) GetOwner() nostr.PubKey { return nostr.MustPubKeyFromHex(config.Info.Pubkey) } func (config *Config) IsOwner(pubkey nostr.PubKey) bool { return pubkey == config.GetOwner() } func (config *Config) GetAssignedRoles(pubkey nostr.PubKey) []Role { roles := make([]Role, 0) for _, role := range config.Roles { if slices.Contains(role.Pubkeys, pubkey.Hex()) { roles = append(roles, role) } } return roles } func (config *Config) GetAllRoles(pubkey nostr.PubKey) []Role { roles := make([]Role, 0) for name, role := range config.Roles { if name == "member" { roles = append(roles, role) } else if slices.Contains(role.Pubkeys, pubkey.Hex()) { roles = append(roles, role) } } return roles } func (config *Config) CanInvite(pubkey nostr.PubKey) bool { if config.IsOwner(pubkey) || config.IsSelf(pubkey) { return true } for _, role := range config.GetAllRoles(pubkey) { if role.CanInvite { return true } } return false } func (config *Config) CanManage(pubkey nostr.PubKey) bool { if config.IsOwner(pubkey) || config.IsSelf(pubkey) { return true } for _, role := range config.GetAllRoles(pubkey) { if role.CanManage { return true } } return false }