diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/db/actions.go | 4 | ||||
| -rw-r--r-- | internal/db/db.go | 8 | ||||
| -rw-r--r-- | internal/db/postgres.go | 56 | ||||
| -rw-r--r-- | internal/model/account.go | 80 | ||||
| -rw-r--r-- | internal/model/avatar.go | 33 | ||||
| -rw-r--r-- | internal/model/header.go | 33 | ||||
| -rw-r--r-- | internal/server/actions.go | 4 | 
7 files changed, 210 insertions, 8 deletions
diff --git a/internal/db/actions.go b/internal/db/actions.go index ae3d8cadf..9a3012465 100644 --- a/internal/db/actions.go +++ b/internal/db/actions.go @@ -28,9 +28,9 @@ import (  // Initialize will initialize the database given in the config for use with GoToSocial  var Initialize action.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error { -	_, err := New(ctx, c, log) +	db, err := New(ctx, c, log)     if err != nil {        return err     } -	return nil +   return db.CreateSchema(ctx)  } diff --git a/internal/db/db.go b/internal/db/db.go index df38ae1fb..4ea4e1af6 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -42,7 +42,15 @@ type DB interface {  	/*  		ANY ADDITIONAL DESIRED FUNCTIONS  	*/ + +	// CreateSchema should populate the database with the required tables +	CreateSchema(context.Context) error + +	// Stop should stop and close the database connection cleanly, returning an error if this is not possible  	Stop(context.Context) error + +	// IsHealthy should return nil if the database connection is healthy, or an error if not +	IsHealthy(context.Context) error  }  // New returns a new database service that satisfies the Service interface and, by extension, diff --git a/internal/db/postgres.go b/internal/db/postgres.go index 2982596d0..5a23b8121 100644 --- a/internal/db/postgres.go +++ b/internal/db/postgres.go @@ -28,8 +28,11 @@ import (  	"time"  	"github.com/go-fed/activity/streams/vocab" -	"github.com/go-pg/pg" +	"github.com/go-pg/pg/extra/pgdebug" +	"github.com/go-pg/pg/v10" +	"github.com/go-pg/pg/v10/orm"  	"github.com/gotosocial/gotosocial/internal/config" +	"github.com/gotosocial/gotosocial/internal/model"  	"github.com/sirupsen/logrus"  ) @@ -47,9 +50,10 @@ func newPostgresService(ctx context.Context, c *config.Config, log *logrus.Entry  	if err != nil {  		return nil, fmt.Errorf("could not create postgres service: %s", err)  	} +	log.Debugf("using pg options: %+v", opts)  	readyChan := make(chan interface{}) -	opts.OnConnect = func(c *pg.Conn) error { +	opts.OnConnect = func(ctx context.Context, c *pg.Conn) error {  		close(readyChan)  		return nil  	} @@ -58,19 +62,30 @@ func newPostgresService(ctx context.Context, c *config.Config, log *logrus.Entry  	pgCtx, cancel := context.WithCancel(ctx)  	conn := pg.Connect(opts).WithContext(pgCtx) +	// this will break the logfmt format we normally log in, +	// since we can't choose where pg outputs to and it defaults to +	// stdout. So use this option with care! +	if log.Logger.GetLevel() >= logrus.TraceLevel { +		conn.AddQueryHook(pgdebug.DebugHook{ +			// Print all queries. +			Verbose: true, +		}) +	} +  	// actually *begin* the connection so that we can tell if the db is there  	// and listening, and also trigger the opts.OnConnect function passed in above -	tx, err := conn.Begin() -	if err != nil { +	if err := conn.Ping(ctx); err != nil {  		cancel()  		return nil, fmt.Errorf("db connection error: %s", err)  	} -	// close the transaction we just started so it doesn't hang around -	if err := tx.Rollback(); err != nil { +	// print out discovered postgres version +	var version string +	if _, err = conn.QueryOneContext(ctx, pg.Scan(&version), "SELECT version()"); err != nil {  		cancel()  		return nil, fmt.Errorf("db connection error: %s", err)  	} +	log.Infof("connected to postgres version: %s", version)  	// make sure the opts.OnConnect function has been triggered  	// and closed the ready channel @@ -82,6 +97,12 @@ func newPostgresService(ctx context.Context, c *config.Config, log *logrus.Entry  		return nil, errors.New("db connection timeout")  	} +	acc := model.StubAccount() +	if _, err := conn.Model(acc).Returning("id").Insert(); err != nil { +		cancel() +		return nil, errors.New("db insert error") +	} +  	// we can confidently return this useable postgres service now  	return &postgresService{  		config: c.DBConfig, @@ -242,3 +263,26 @@ func (ps *postgresService) Stop(ctx context.Context) error {  	}  	return nil  } + +func (ps *postgresService) CreateSchema(ctx context.Context) error { +	models := []interface{}{ +		(*model.Account)(nil), +	} +	ps.log.Info("creating db schema") + +	for _, model := range models { +		err := ps.conn.Model(model).CreateTable(&orm.CreateTableOptions{ +			IfNotExists: true, +		}) +		if err != nil { +			return err +		} +	} + +	ps.log.Info("db schema created") +	return nil +} + +func (ps *postgresService) IsHealthy(ctx context.Context) error { +	return ps.conn.Ping(ctx) +} diff --git a/internal/model/account.go b/internal/model/account.go new file mode 100644 index 000000000..55ee62773 --- /dev/null +++ b/internal/model/account.go @@ -0,0 +1,80 @@ +/* +   GoToSocial +   Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU Affero General Public License as published by +   the Free Software Foundation, either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU Affero General Public License for more details. + +   You should have received a copy of the GNU Affero General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +package model + +import ( +	"net/url" +	"time" +) + +// Account represents a user account +type Account struct { +	Avatar +	Header +	ID                    int `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"` +	Username              string +	Domain                string +	Secret                string +	PrivateKey            string +	PublicKey             string +	RemoteURL             *url.URL  `pg:"type:text"` +	SalmonURL             *url.URL  `pg:"type:text"` +	HubURL                *url.URL  `pg:"type:text"` +	CreatedAt             time.Time `pg:"type:timestamp,notnull"` +	UpdatedAt             time.Time `pg:"type:timestamp,notnull"` +	Note                  string +	DisplayName           string +	URI                   *url.URL  `pg:"type:text"` +	URL                   *url.URL  `pg:"type:text"` +	SubscriptionExpiresAt time.Time `pg:"type:timestamp"` +	Locked                bool +	LastWebfingeredAt     time.Time `pg:"type:timestamp"` +	InboxURL              *url.URL  `pg:"type:text"` +	OutboxURL             *url.URL  `pg:"type:text"` +	SharedInboxURL        *url.URL  `pg:"type:text"` +	FollowersURL          *url.URL  `pg:"type:text"` +	Protocol              int +	Memorial              bool +	MovedToAccountID      int +	FeaturedCollectionURL *url.URL `pg:"type:text"` +	Fields                map[string]string +	ActorType             string +	Discoverable          bool +	AlsoKnownAs           string +	SilencedAt            time.Time `pg:"type:timestamp"` +	SuspendedAt           time.Time `pg:"type:timestamp"` +	TrustLevel            int +	HideCollections       bool +	DevicesURL            *url.URL  `pg:"type:text"` +	SensitizedAt          time.Time `pg:"type:timestamp"` +	SuspensionOrigin      int +} + +func StubAccount() *Account { + +	remoteURL, _ := url.Parse("https://example.org/@someuser") + +	return &Account{ +		Username:  "some_user", +		Domain:    "example.org", +		RemoteURL: remoteURL, +		CreatedAt: time.Now(), +		UpdatedAt: time.Now(), +	} +} diff --git a/internal/model/avatar.go b/internal/model/avatar.go new file mode 100644 index 000000000..637fa5e3c --- /dev/null +++ b/internal/model/avatar.go @@ -0,0 +1,33 @@ +/* +   GoToSocial +   Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU Affero General Public License as published by +   the Free Software Foundation, either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU Affero General Public License for more details. + +   You should have received a copy of the GNU Affero General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +package model + +import ( +	"net/url" +	"time" +) + +type Avatar struct { +	AvatarFileName             string +	AvatarContentType          string +	AvatarFileSize             int +	AvatarUpdatedAt            *time.Time `pg:"type:timestamp"` +	AvatarRemoteURL            *url.URL   `pg:"type:text"` +	AvatarStorageSchemaVersion int +} diff --git a/internal/model/header.go b/internal/model/header.go new file mode 100644 index 000000000..edaa7a2dc --- /dev/null +++ b/internal/model/header.go @@ -0,0 +1,33 @@ +/* +   GoToSocial +   Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU Affero General Public License as published by +   the Free Software Foundation, either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU Affero General Public License for more details. + +   You should have received a copy of the GNU Affero General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +package model + +import ( +	"net/url" +	"time" +) + +type Header struct { +	HeaderFileName             string +	HeaderContentType          string +	HeaderFileSize             int +	HeaderUpdatedAt            *time.Time `pg:"type:timestamp"` +	HeaderRemoteURL            *url.URL   `pg:"type:text"` +	HeaderStorageSchemaVersion int +} diff --git a/internal/server/actions.go b/internal/server/actions.go index b3047c334..6ff3045b5 100644 --- a/internal/server/actions.go +++ b/internal/server/actions.go @@ -38,6 +38,10 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr  		return fmt.Errorf("error creating dbservice: %s", err)  	} +	if err := dbService.CreateSchema(ctx); err != nil { +		return fmt.Errorf("error creating dbschema: %s", err) +	} +  	// catch shutdown signals from the operating system  	sigs := make(chan os.Signal, 1)  	signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)  | 
