Initial commit: Orchard content-addressable storage system

- Go server with Gin framework
- PostgreSQL for metadata storage
- MinIO/S3 for artifact storage with SHA256 content addressing
- REST API for grove/tree/fruit operations
- Web UI for managing artifacts
- Docker Compose setup for local development
This commit is contained in:
Mondo Diaz
2025-12-04 10:14:49 -06:00
commit f8e9650de3
19 changed files with 3531 additions and 0 deletions

104
internal/config/config.go Normal file
View File

@@ -0,0 +1,104 @@
package config
import (
"strings"
"github.com/spf13/viper"
)
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
S3 S3Config `mapstructure:"s3"`
Redis RedisConfig `mapstructure:"redis"`
}
type ServerConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
}
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"dbname"`
SSLMode string `mapstructure:"sslmode"`
}
type S3Config struct {
Endpoint string `mapstructure:"endpoint"`
Region string `mapstructure:"region"`
Bucket string `mapstructure:"bucket"`
AccessKeyID string `mapstructure:"access_key_id"`
SecretAccessKey string `mapstructure:"secret_access_key"`
UsePathStyle bool `mapstructure:"use_path_style"`
}
type RedisConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Password string `mapstructure:"password"`
DB int `mapstructure:"db"`
}
func Load() (*Config, error) {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AddConfigPath("/etc/orchard")
// Environment variable overrides
viper.SetEnvPrefix("ORCHARD")
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// Bind environment variables explicitly for nested config
viper.BindEnv("server.host", "ORCHARD_SERVER_HOST")
viper.BindEnv("server.port", "ORCHARD_SERVER_PORT")
viper.BindEnv("database.host", "ORCHARD_DATABASE_HOST")
viper.BindEnv("database.port", "ORCHARD_DATABASE_PORT")
viper.BindEnv("database.user", "ORCHARD_DATABASE_USER")
viper.BindEnv("database.password", "ORCHARD_DATABASE_PASSWORD")
viper.BindEnv("database.dbname", "ORCHARD_DATABASE_DBNAME")
viper.BindEnv("database.sslmode", "ORCHARD_DATABASE_SSLMODE")
viper.BindEnv("s3.endpoint", "ORCHARD_S3_ENDPOINT")
viper.BindEnv("s3.region", "ORCHARD_S3_REGION")
viper.BindEnv("s3.bucket", "ORCHARD_S3_BUCKET")
viper.BindEnv("s3.access_key_id", "ORCHARD_S3_ACCESS_KEY_ID")
viper.BindEnv("s3.secret_access_key", "ORCHARD_S3_SECRET_ACCESS_KEY")
viper.BindEnv("s3.use_path_style", "ORCHARD_S3_USE_PATH_STYLE")
viper.BindEnv("redis.host", "ORCHARD_REDIS_HOST")
viper.BindEnv("redis.port", "ORCHARD_REDIS_PORT")
viper.BindEnv("redis.password", "ORCHARD_REDIS_PASSWORD")
viper.BindEnv("redis.db", "ORCHARD_REDIS_DB")
// Defaults
viper.SetDefault("server.host", "0.0.0.0")
viper.SetDefault("server.port", 8080)
viper.SetDefault("database.host", "localhost")
viper.SetDefault("database.port", 5432)
viper.SetDefault("database.user", "orchard")
viper.SetDefault("database.dbname", "orchard")
viper.SetDefault("database.sslmode", "disable")
viper.SetDefault("s3.region", "us-east-1")
viper.SetDefault("s3.bucket", "orchard-artifacts")
viper.SetDefault("s3.use_path_style", true)
viper.SetDefault("redis.host", "localhost")
viper.SetDefault("redis.port", 6379)
viper.SetDefault("redis.db", 0)
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, err
}
}
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}