gotosocial/internal/db/db.go

173 lines
8.2 KiB
Go
Raw Normal View History

2021-03-02 18:26:30 +01:00
/*
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 db
import (
2021-03-02 22:52:31 +01:00
"context"
2021-03-02 18:26:30 +01:00
"fmt"
2021-03-26 19:02:20 +01:00
"net"
2021-03-02 18:26:30 +01:00
"strings"
"github.com/go-fed/activity/pub"
2021-03-04 14:38:18 +01:00
"github.com/gotosocial/gotosocial/internal/config"
2021-03-23 22:13:01 +01:00
"github.com/gotosocial/gotosocial/internal/db/model"
"github.com/gotosocial/gotosocial/pkg/mastotypes"
2021-03-02 22:52:31 +01:00
"github.com/sirupsen/logrus"
2021-03-02 18:26:30 +01:00
)
const dbTypePostgres string = "POSTGRES"
2021-03-25 20:08:23 +01:00
// ErrNoEntries is to be returned from the DB interface when no entries are found for a given query.
2021-03-23 22:13:01 +01:00
type ErrNoEntries struct{}
func (e ErrNoEntries) Error() string {
return "no entries"
}
// DB provides methods for interacting with an underlying database or other storage mechanism (for now, just postgres).
2021-03-23 22:13:01 +01:00
// Note that in all of the functions below, the passed interface should be a pointer or a slice, which will then be populated
// by whatever is returned from the database.
2021-03-04 14:38:18 +01:00
type DB interface {
// Federation returns an interface that's compatible with go-fed, for performing federation storage/retrieval functions.
// See: https://pkg.go.dev/github.com/go-fed/activity@v1.0.0/pub?utm_source=gopls#Database
Federation() pub.Database
2021-03-02 18:26:30 +01:00
2021-03-23 22:13:01 +01:00
/*
BASIC DB FUNCTIONALITY
*/
// CreateTable creates a table for the given interface.
// For implementations that don't use tables, this can just return nil.
CreateTable(i interface{}) error
2021-03-05 18:31:12 +01:00
2021-03-23 22:13:01 +01:00
// DropTable drops the table for the given interface.
// For implementations that don't use tables, this can just return nil.
DropTable(i interface{}) error
2021-03-05 18:31:12 +01:00
2021-03-23 22:13:01 +01:00
// Stop should stop and close the database connection cleanly, returning an error if this is not possible.
// If the database implementation doesn't need to be stopped, this can just return nil.
Stop(ctx context.Context) error
2021-03-05 18:31:12 +01:00
2021-03-23 22:13:01 +01:00
// IsHealthy should return nil if the database connection is healthy, or an error if not.
IsHealthy(ctx context.Context) error
2021-03-23 22:13:01 +01:00
// GetByID gets one entry by its id. In a database like postgres, this might be the 'id' field of the entry,
// for other implementations (for example, in-memory) it might just be the key of a map.
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
// In case of no entries, a 'no entries' error will be returned
GetByID(id string, i interface{}) error
2021-03-23 22:13:01 +01:00
// GetWhere gets one entry where key = value. This is similar to GetByID but allows the caller to specify the
// name of the key to select from.
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
// In case of no entries, a 'no entries' error will be returned
GetWhere(key string, value interface{}, i interface{}) error
2021-03-23 22:13:01 +01:00
// GetAll will try to get all entries of type i.
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
// In case of no entries, a 'no entries' error will be returned
GetAll(i interface{}) error
2021-03-23 22:13:01 +01:00
// Put simply stores i. It is up to the implementation to figure out how to store it, and using what key.
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
Put(i interface{}) error
2021-03-23 22:13:01 +01:00
// UpdateByID updates i with id id.
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
UpdateByID(id string, i interface{}) error
2021-03-23 22:13:01 +01:00
// DeleteByID removes i with id id.
// If i didn't exist anyway, then no error should be returned.
DeleteByID(id string, i interface{}) error
2021-03-23 22:13:01 +01:00
// DeleteWhere deletes i where key = value
// If i didn't exist anyway, then no error should be returned.
DeleteWhere(key string, value interface{}, i interface{}) error
2021-03-23 13:17:54 +01:00
2021-03-23 22:13:01 +01:00
/*
HANDY SHORTCUTS
*/
// GetAccountByUserID is a shortcut for the common action of fetching an account corresponding to a user ID.
// The given account pointer will be set to the result of the query, whatever it is.
// In case of no entries, a 'no entries' error will be returned
GetAccountByUserID(userID string, account *model.Account) error
// GetFollowingByAccountID is a shortcut for the common action of fetching a list of accounts that accountID is following.
// The given slice 'following' will be set to the result of the query, whatever it is.
// In case of no entries, a 'no entries' error will be returned
GetFollowingByAccountID(accountID string, following *[]model.Follow) error
// GetFollowersByAccountID is a shortcut for the common action of fetching a list of accounts that accountID is followed by.
// The given slice 'followers' will be set to the result of the query, whatever it is.
// In case of no entries, a 'no entries' error will be returned
GetFollowersByAccountID(accountID string, followers *[]model.Follow) error
// GetStatusesByAccountID is a shortcut for the common action of fetching a list of statuses produced by accountID.
// The given slice 'statuses' will be set to the result of the query, whatever it is.
// In case of no entries, a 'no entries' error will be returned
GetStatusesByAccountID(accountID string, statuses *[]model.Status) error
// GetStatusesByTimeDescending is a shortcut for getting the most recent statuses. accountID is optional, if not provided
// then all statuses will be returned. If limit is set to 0, the size of the returned slice will not be limited. This can
// be very memory intensive so you probably shouldn't do this!
// In case of no entries, a 'no entries' error will be returned
GetStatusesByTimeDescending(accountID string, statuses *[]model.Status, limit int) error
// GetLastStatusForAccountID simply gets the most recent status by the given account.
// The given slice 'status' pointer will be set to the result of the query, whatever it is.
// In case of no entries, a 'no entries' error will be returned
GetLastStatusForAccountID(accountID string, status *model.Status) error
2021-03-25 23:33:26 +01:00
// IsUsernameAvailable checks whether a given username is available on our domain.
// Returns an error if the username is already taken, or something went wrong in the db.
IsUsernameAvailable(username string) error
2021-03-25 23:49:29 +01:00
// IsEmailAvailable checks whether a given email address for a new account is available to be used on our domain.
// Return an error if:
// A) the email is already associated with an account
// B) we block signups from this email domain
// C) something went wrong in the db
2021-03-25 23:33:26 +01:00
IsEmailAvailable(email string) error
2021-03-26 19:02:20 +01:00
// NewSignup creates a new user in the database with the given parameters, with an *unconfirmed* email address.
// By the time this function is called, it should be assumed that all the parameters have passed validation!
NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string) (*model.User, error)
2021-03-23 22:13:01 +01:00
/*
USEFUL CONVERSION FUNCTIONS
*/
// AccountToMastoSensitive takes a db model account as a param, and returns a populated mastotype account, or an error
// if something goes wrong. The returned account should be ready to serialize on an API level, and may have sensitive fields,
// so serve it only to an authorized user who should have permission to see it.
AccountToMastoSensitive(account *model.Account) (*mastotypes.Account, error)
2021-03-02 18:26:30 +01:00
}
// New returns a new database service that satisfies the DB interface and, by extension,
2021-03-02 18:26:30 +01:00
// the go-fed database interface described here: https://github.com/go-fed/activity/blob/master/pub/database.go
2021-03-04 14:38:18 +01:00
func New(ctx context.Context, c *config.Config, log *logrus.Logger) (DB, error) {
switch strings.ToUpper(c.DBConfig.Type) {
2021-03-02 18:26:30 +01:00
case dbTypePostgres:
2021-03-04 14:38:18 +01:00
return newPostgresService(ctx, c, log.WithField("service", "db"))
2021-03-02 18:26:30 +01:00
default:
2021-03-04 14:38:18 +01:00
return nil, fmt.Errorf("database type %s not supported", c.DBConfig.Type)
2021-03-02 18:26:30 +01:00
}
}