create db schemas for accounts

This commit is contained in:
tsmethurst
2021-03-05 18:31:12 +01:00
parent 052783db66
commit 59963090cb
10 changed files with 284 additions and 21 deletions

View File

@ -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)
}

View File

@ -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,

View File

@ -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)
}

80
internal/model/account.go Normal file
View File

@ -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(),
}
}

33
internal/model/avatar.go Normal file
View File

@ -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
}

33
internal/model/header.go Normal file
View File

@ -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
}

View File

@ -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)