Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Cocoon is a PDS implementation in Go. It is highly experimental, and is not read

**For PostgreSQL deployment:**
```bash
# Add POSTGRES_PASSWORD to your .env file first!
# See database configuration first!
docker-compose -f docker-compose.postgres.yaml up -d
```

Expand Down Expand Up @@ -114,8 +114,14 @@ COCOON_DB_TYPE="postgres"
# Format: postgres://user:password@host:port/database?sslmode=disable
COCOON_DATABASE_URL="postgres://cocoon:password@localhost:5432/cocoon?sslmode=disable"

# Or use the standard DATABASE_URL environment variable
DATABASE_URL="postgres://cocoon:password@localhost:5432/cocoon?sslmode=disable"
# You can also specify them with:
COCOON_DB_HOST="localhost"
COCOON_DB_USER="cocoon"
COCOON_DB_PASSWORD="password"
COCOON_DB_PORT=5432
COCOON_DB_NAME="cocoon"
# if you want to set custom DSN option like timezone or sslmode (if nothing is set, sslmode is disabled)
COCOON_DB_CUSTOM=""
```

For SQLite (default):
Expand Down
66 changes: 44 additions & 22 deletions cmd/cocoon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/urfave/cli/v2"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)

Expand Down Expand Up @@ -54,6 +52,33 @@ func main() {
Usage: "PostgreSQL connection string (required if db-type is postgres)",
EnvVars: []string{"COCOON_DATABASE_URL", "DATABASE_URL"},
},
&cli.StringFlag{
Name: "db-host",
Usage: "PostgreSQL host (required if db-type is postgres and if database-url is not set)",
EnvVars: []string{"COCOON_DB_HOST"},
},
&cli.StringFlag{
Name: "db-user",
Usage: "PostgreSQL user (required if db-type is postgres and if database-url is not set)",
EnvVars: []string{"COCOON_DB_USER"},
},
&cli.StringFlag{
Name: "db-password",
Usage: "PostgreSQL password (required if db-type is postgres and if database-url is not set)",
EnvVars: []string{"COCOON_DB_PASSWORD"},
},
&cli.UintFlag{
Name: "db-port",
Value: 5432,
Usage: "PostgreSQL port (default to 5432, only has effect if db-type is postgres)",
EnvVars: []string{"COCOON_DB_PORT"},
},
&cli.StringFlag{
Name: "db-custom",
Value: "sslmode=disable",
Usage: "PostgreSQL custom DSN (default to sslmode=disable, only has effect if db-type is postgres)",
EnvVars: []string{"COCOON_DB_CUSTOM"},
},
&cli.StringFlag{
Name: "did",
EnvVars: []string{"COCOON_DID"},
Expand Down Expand Up @@ -215,9 +240,6 @@ var runServe = &cli.Command{
Logger: logger,
LogLevel: level,
Addr: cmd.String("addr"),
DbName: cmd.String("db-name"),
DbType: cmd.String("db-type"),
DatabaseURL: cmd.String("database-url"),
Did: cmd.String("did"),
Hostname: cmd.String("hostname"),
RotationKeyPath: cmd.String("rotation-key-path"),
Expand All @@ -233,6 +255,7 @@ var runServe = &cli.Command{
SmtpPort: cmd.String("smtp-port"),
SmtpEmail: cmd.String("smtp-email"),
SmtpName: cmd.String("smtp-name"),
DBConfig: newDbArgs(cmd),
S3Config: &server.S3Config{
BackupsEnabled: cmd.Bool("s3-backups-enabled"),
BlobstoreEnabled: cmd.Bool("s3-blobstore-enabled"),
Expand Down Expand Up @@ -409,24 +432,23 @@ var runResetPassword = &cli.Command{
},
}

func newDb(cmd *cli.Context) (*gorm.DB, error) {
dbType := cmd.String("db-type")
if dbType == "" {
dbType = "sqlite"
func newDbArgs(cmd *cli.Context) *server.DBConfig {
return &server.DBConfig{
Name: cmd.String("db-name"),
Type: cmd.String("db-type"),
URL: cmd.String("database-url"),
Host: cmd.String("db-host"),
User: cmd.String("db-user"),
Password: cmd.String("db-password"),
Port: cmd.Uint("db-port"),
Custom: cmd.String("db-custom"),
}
}

switch dbType {
case "postgres":
databaseURL := cmd.String("database-url")
if databaseURL == "" {
return nil, fmt.Errorf("COCOON_DATABASE_URL or DATABASE_URL must be set when using postgres")
}
return gorm.Open(postgres.Open(databaseURL), &gorm.Config{})
default:
dbName := cmd.String("db-name")
if dbName == "" {
dbName = "cocoon.db"
}
return gorm.Open(sqlite.Open(dbName), &gorm.Config{})
func newDb(cmd *cli.Context) (*gorm.DB, error) {
db := newDbArgs(cmd)
if db.Type == "" {
db.Type = "sqlite"
}
return db.Connect(nil)
}
2 changes: 0 additions & 2 deletions docker-compose.noproxy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ services:
volumes:
- ./keys:/keys
- ./data:/data/cocoon
- ./init-keys.sh:/init-keys.sh:ro
environment:
COCOON_DID: ${COCOON_DID}
COCOON_HOSTNAME: ${COCOON_HOSTNAME}
Expand Down Expand Up @@ -100,7 +99,6 @@ services:
volumes:
- ./keys:/keys
- ./data:/data/cocoon
- ./create-initial-invite.sh:/create-initial-invite.sh:ro
environment:
COCOON_DID: ${COCOON_DID}
COCOON_HOSTNAME: ${COCOON_HOSTNAME}
Expand Down
3 changes: 1 addition & 2 deletions docker-compose.postgres.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ services:
volumes:
- ./keys:/keys
- ./data:/data/cocoon
- ./init-keys.sh:/init-keys.sh:ro
environment:
COCOON_DID: ${COCOON_DID}
COCOON_HOSTNAME: ${COCOON_HOSTNAME}
Expand Down Expand Up @@ -118,7 +117,7 @@ services:
container_name: cocoon-create-invite
volumes:
- ./keys:/keys
- ./create-initial-invite.sh:/create-initial-invite.sh:ro
- ./data:/data/cocoon
environment:
COCOON_DID: ${COCOON_DID}
COCOON_HOSTNAME: ${COCOON_HOSTNAME}
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ services:
volumes:
- ./keys:/keys
- ./data:/data/cocoon
- ./init-keys.sh:/init-keys.sh:ro
environment:
COCOON_DID: ${COCOON_DID}
COCOON_HOSTNAME: ${COCOON_HOSTNAME}
Expand Down Expand Up @@ -92,7 +91,6 @@ services:
volumes:
- ./keys:/keys
- ./data:/data/cocoon
- ./create-initial-invite.sh:/create-initial-invite.sh:ro
environment:
COCOON_DID: ${COCOON_DID}
COCOON_HOSTNAME: ${COCOON_HOSTNAME}
Expand Down
75 changes: 75 additions & 0 deletions server/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package server

import (
"fmt"
"log/slog"

"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)

type DBConfig struct {
// Name is the sqlite file or the name of the postgres database
Name string
Type string
URL string
User string
Password string
Host string
Port uint
Custom string
}

func (cfg *DBConfig) Connect(logger *slog.Logger) (*gorm.DB, error) {
dbType := cfg.Type
if dbType == "" {
dbType = "sqlite"
}

var gdb *gorm.DB
var err error
switch dbType {
case "postgres":
var dsn string
if cfg.URL != "" {
dsn = cfg.URL
} else if cfg.Host != "" &&
cfg.User != "" &&
cfg.Password != "" &&
cfg.Name != "" {
dsn = fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s %s",
cfg.Host,
cfg.Port,
cfg.User,
cfg.Password,
cfg.Name,
cfg.Custom,
)
} else {
return nil, fmt.Errorf("database config must be set when using postgres")
}
gdb, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("failed to connect to postgres: %w", err)
}
if logger != nil {
logger.Info("connected to PostgreSQL database", "host", cfg.Host, "dbname", cfg.Name)
}
case "sqlite":
gdb, err = gorm.Open(sqlite.Open(cfg.Name), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("failed to open sqlite database: %w", err)
}
gdb.Exec("PRAGMA journal_mode=WAL")
gdb.Exec("PRAGMA synchronous=NORMAL")

if logger != nil {
logger.Info("connected to SQLite database", "path", cfg.Name)
}
default:
panic("UNSUPPORTED DB TYPE")
}
return gdb, nil
}
38 changes: 5 additions & 33 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
slogecho "github.com/samber/slog-echo"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)

const (
Expand Down Expand Up @@ -94,9 +91,6 @@ type Args struct {

LogLevel slog.Level
Addr string
DbName string
DbType string
DatabaseURL string
Version string
Did string
Hostname string
Expand All @@ -114,6 +108,7 @@ type Args struct {
SmtpEmail string
SmtpName string

DBConfig *DBConfig
S3Config *S3Config

SessionSecret string
Expand Down Expand Up @@ -253,7 +248,7 @@ func New(args *Args) (*Server, error) {
return nil, fmt.Errorf("addr must be set")
}

if args.DbName == "" {
if args.DBConfig.Name == "" {
return nil, fmt.Errorf("db name must be set")
}

Expand Down Expand Up @@ -332,32 +327,9 @@ func New(args *Args) (*Server, error) {
IdleTimeout: 5 * time.Minute,
}

dbType := args.DbType
if dbType == "" {
dbType = "sqlite"
}

var gdb *gorm.DB
var err error
switch dbType {
case "postgres":
if args.DatabaseURL == "" {
return nil, fmt.Errorf("database-url must be set when using postgres")
}
gdb, err = gorm.Open(postgres.Open(args.DatabaseURL), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("failed to connect to postgres: %w", err)
}
logger.Info("connected to PostgreSQL database")
default:
gdb, err = gorm.Open(sqlite.Open(args.DbName), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("failed to open sqlite database: %w", err)
}
gdb.Exec("PRAGMA journal_mode=WAL")
gdb.Exec("PRAGMA synchronous=NORMAL")

logger.Info("connected to SQLite database", "path", args.DbName)
gdb, err := args.DBConfig.Connect(logger)
if err != nil {
return nil, err
}
dbw := db.NewDB(gdb)

Expand Down