| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | /* | 
					
						
							|  |  |  |    GoToSocial | 
					
						
							| 
									
										
										
										
											2021-12-20 18:42:19 +01:00
										 |  |  |    Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |    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/>. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | package bundb | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 	"crypto/tls" | 
					
						
							|  |  |  | 	"crypto/x509" | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"database/sql" | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 	"encoding/pem" | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2021-09-20 18:20:21 +02:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2021-03-03 18:12:02 +01:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2021-03-02 22:52:31 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 11:21:36 +02:00
										 |  |  | 	"github.com/google/uuid" | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"github.com/jackc/pgx/v4" | 
					
						
							|  |  |  | 	"github.com/jackc/pgx/v4/stdlib" | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/cache" | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | 
					
						
							| 
									
										
										
										
											2021-08-31 19:27:02 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/id" | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"github.com/uptrace/bun" | 
					
						
							|  |  |  | 	"github.com/uptrace/bun/dialect/pgdialect" | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	"github.com/uptrace/bun/dialect/sqlitedialect" | 
					
						
							| 
									
										
										
										
											2021-08-31 19:27:02 +02:00
										 |  |  | 	"github.com/uptrace/bun/migrate" | 
					
						
							| 
									
										
										
										
											2021-09-30 11:16:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 	grufcache "codeberg.org/gruf/go-cache/v2" | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	"modernc.org/sqlite" | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	dbTypePostgres = "postgres" | 
					
						
							|  |  |  | 	dbTypeSqlite   = "sqlite" | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// dbTLSModeDisable does not attempt to make a TLS connection to the database. | 
					
						
							|  |  |  | 	dbTLSModeDisable = "disable" | 
					
						
							|  |  |  | 	// dbTLSModeEnable attempts to make a TLS connection to the database, but doesn't fail if | 
					
						
							|  |  |  | 	// the certificate passed by the database isn't verified. | 
					
						
							|  |  |  | 	dbTLSModeEnable = "enable" | 
					
						
							|  |  |  | 	// dbTLSModeRequire attempts to make a TLS connection to the database, and requires | 
					
						
							|  |  |  | 	// that the certificate presented by the database is valid. | 
					
						
							|  |  |  | 	dbTLSModeRequire = "require" | 
					
						
							|  |  |  | 	// dbTLSModeUnset means that the TLS mode has not been set. | 
					
						
							|  |  |  | 	dbTLSModeUnset = "" | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | var registerTables = []interface{}{ | 
					
						
							| 
									
										
										
										
											2022-09-26 11:56:01 +02:00
										 |  |  | 	>smodel.AccountToEmoji{}, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	>smodel.StatusToEmoji{}, | 
					
						
							|  |  |  | 	>smodel.StatusToTag{}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-26 11:56:01 +02:00
										 |  |  | // DBService satisfies the DB interface | 
					
						
							|  |  |  | type DBService struct { | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	db.Account | 
					
						
							|  |  |  | 	db.Admin | 
					
						
							|  |  |  | 	db.Basic | 
					
						
							|  |  |  | 	db.Domain | 
					
						
							| 
									
										
										
										
											2022-05-20 04:34:36 -04:00
										 |  |  | 	db.Emoji | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	db.Instance | 
					
						
							|  |  |  | 	db.Media | 
					
						
							|  |  |  | 	db.Mention | 
					
						
							|  |  |  | 	db.Notification | 
					
						
							|  |  |  | 	db.Relationship | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	db.Session | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	db.Status | 
					
						
							|  |  |  | 	db.Timeline | 
					
						
							| 
									
										
										
										
											2022-10-03 10:46:11 +02:00
										 |  |  | 	db.User | 
					
						
							| 
									
										
										
										
											2022-11-11 12:18:38 +01:00
										 |  |  | 	db.Tombstone | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	conn *DBConn | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-26 11:56:01 +02:00
										 |  |  | // GetConn returns the underlying bun connection. | 
					
						
							|  |  |  | // Should only be used in testing + exceptional circumstance. | 
					
						
							|  |  |  | func (dbService *DBService) GetConn() *DBConn { | 
					
						
							|  |  |  | 	return dbService.conn | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-11 05:37:33 -07:00
										 |  |  | func doMigration(ctx context.Context, db *bun.DB) error { | 
					
						
							| 
									
										
										
										
											2021-08-31 19:27:02 +02:00
										 |  |  | 	migrator := migrate.NewMigrator(db, migrations.Migrations) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := migrator.Init(ctx); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	group, err := migrator.Migrate(ctx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-09-01 18:29:25 +02:00
										 |  |  | 		if err.Error() == "migrate: there are no any migrations" { | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-31 19:27:02 +02:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if group.ID == 0 { | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 		log.Info("there are no new migrations to run") | 
					
						
							| 
									
										
										
										
											2021-08-31 19:27:02 +02:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	log.Infof("MIGRATED DATABASE TO %s", group) | 
					
						
							| 
									
										
										
										
											2021-08-31 19:27:02 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | // NewBunDBService returns a bunDB derived from the provided config, which implements the go-fed DB interface. | 
					
						
							|  |  |  | // Under the hood, it uses https://github.com/uptrace/bun to create and maintain a database connection. | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | func NewBunDBService(ctx context.Context) (db.DB, error) { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	var conn *DBConn | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	dbType := strings.ToLower(config.GetDbType()) | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch dbType { | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	case dbTypePostgres: | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 		conn, err = pgConn(ctx) | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case dbTypeSqlite: | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 		conn, err = sqliteConn(ctx) | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 		return nil, fmt.Errorf("database type %s not supported for bundb", dbType) | 
					
						
							| 
									
										
										
										
											2021-03-05 18:31:12 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 	// Add database query hook | 
					
						
							|  |  |  | 	conn.DB.AddQueryHook(queryHook{}) | 
					
						
							| 
									
										
										
										
											2021-09-11 13:19:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	// table registration is needed for many-to-many, see: | 
					
						
							|  |  |  | 	// https://bun.uptrace.dev/orm/many-to-many-relation/ | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	for _, t := range registerTables { | 
					
						
							|  |  |  | 		conn.RegisterModel(t) | 
					
						
							| 
									
										
										
										
											2021-03-02 22:52:31 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	// perform any pending database migrations: this includes | 
					
						
							|  |  |  | 	// the very first 'migration' on startup which just creates | 
					
						
							|  |  |  | 	// necessary tables | 
					
						
							| 
									
										
										
										
											2021-10-11 05:37:33 -07:00
										 |  |  | 	if err := doMigration(ctx, conn.DB); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-31 19:27:02 +02:00
										 |  |  | 		return nil, fmt.Errorf("db migration error: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 	// Prepare caches required by more than one struct | 
					
						
							|  |  |  | 	userCache := cache.NewUserCache() | 
					
						
							|  |  |  | 	accountCache := cache.NewAccountCache() | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 	// Prepare other caches | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 	// Prepare mentions cache | 
					
						
							|  |  |  | 	// TODO: move into internal/cache | 
					
						
							|  |  |  | 	mentionCache := grufcache.New[string, *gtsmodel.Mention]() | 
					
						
							|  |  |  | 	mentionCache.SetTTL(time.Minute*5, false) | 
					
						
							|  |  |  | 	mentionCache.Start(time.Second * 10) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Prepare notifications cache | 
					
						
							|  |  |  | 	// TODO: move into internal/cache | 
					
						
							|  |  |  | 	notifCache := grufcache.New[string, *gtsmodel.Notification]() | 
					
						
							|  |  |  | 	notifCache.SetTTL(time.Minute*5, false) | 
					
						
							|  |  |  | 	notifCache.Start(time.Second * 10) | 
					
						
							| 
									
										
										
										
											2021-09-01 11:13:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 	// Create DB structs that require ptrs to each other | 
					
						
							|  |  |  | 	accounts := &accountDB{conn: conn, cache: accountCache} | 
					
						
							|  |  |  | 	status := &statusDB{conn: conn, cache: cache.NewStatusCache()} | 
					
						
							| 
									
										
										
										
											2022-11-14 23:47:27 +01:00
										 |  |  | 	emoji := &emojiDB{conn: conn, emojiCache: cache.NewEmojiCache(), categoryCache: cache.NewEmojiCategoryCache()} | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 	timeline := &timelineDB{conn: conn} | 
					
						
							| 
									
										
										
										
											2022-11-11 12:18:38 +01:00
										 |  |  | 	tombstone := &tombstoneDB{conn: conn} | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Setup DB cross-referencing | 
					
						
							|  |  |  | 	accounts.status = status | 
					
						
							|  |  |  | 	status.accounts = accounts | 
					
						
							|  |  |  | 	timeline.status = status | 
					
						
							| 
									
										
										
										
											2022-09-02 11:17:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-11 12:18:38 +01:00
										 |  |  | 	// Initialize db structs | 
					
						
							|  |  |  | 	tombstone.init() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-26 11:56:01 +02:00
										 |  |  | 	ps := &DBService{ | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 		Account: accounts, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		Admin: &adminDB{ | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 			conn:         conn, | 
					
						
							|  |  |  | 			userCache:    userCache, | 
					
						
							|  |  |  | 			accountCache: accountCache, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Basic: &basicDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn: conn, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Domain: &domainDB{ | 
					
						
							| 
									
										
										
										
											2022-09-02 11:17:46 +01:00
										 |  |  | 			conn:  conn, | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 			cache: cache.NewDomainBlockCache(), | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-09-06 12:42:55 +02:00
										 |  |  | 		Emoji: emoji, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		Instance: &instanceDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn: conn, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Media: &mediaDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn: conn, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Mention: &mentionDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn:  conn, | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 			cache: mentionCache, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Notification: ¬ificationDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn:  conn, | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 			cache: notifCache, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Relationship: &relationshipDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn: conn, | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Session: &sessionDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn: conn, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 		Status:   status, | 
					
						
							|  |  |  | 		Timeline: timeline, | 
					
						
							| 
									
										
										
										
											2022-10-03 10:46:11 +02:00
										 |  |  | 		User: &userDB{ | 
					
						
							|  |  |  | 			conn:  conn, | 
					
						
							|  |  |  | 			cache: userCache, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-11-11 12:18:38 +01:00
										 |  |  | 		Tombstone: tombstone, | 
					
						
							|  |  |  | 		conn:      conn, | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	// we can confidently return this useable service now | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	return ps, nil | 
					
						
							| 
									
										
										
										
											2021-03-22 22:26:54 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | func sqliteConn(ctx context.Context) (*DBConn, error) { | 
					
						
							| 
									
										
										
										
											2022-01-30 17:06:28 +01:00
										 |  |  | 	// validate db address has actually been set | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	dbAddress := config.GetDbAddress() | 
					
						
							| 
									
										
										
										
											2022-01-30 17:06:28 +01:00
										 |  |  | 	if dbAddress == "" { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 		return nil, fmt.Errorf("'%s' was not set when attempting to start sqlite", config.DbAddressFlag()) | 
					
						
							| 
									
										
										
										
											2022-01-30 17:06:28 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	// Drop anything fancy from DB address | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	dbAddress = strings.Split(dbAddress, "?")[0] | 
					
						
							|  |  |  | 	dbAddress = strings.TrimPrefix(dbAddress, "file:") | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Append our own SQLite preferences | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	dbAddress = "file:" + dbAddress + "?cache=shared" | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 11:21:36 +02:00
										 |  |  | 	var inMem bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if dbAddress == "file::memory:?cache=shared" { | 
					
						
							|  |  |  | 		dbAddress = fmt.Sprintf("file:%s?mode=memory&cache=shared", uuid.NewString()) | 
					
						
							|  |  |  | 		log.Infof("using in-memory database address " + dbAddress) | 
					
						
							|  |  |  | 		log.Warn("sqlite in-memory database should only be used for debugging") | 
					
						
							|  |  |  | 		inMem = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	// Open new DB instance | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	sqldb, err := sql.Open("sqlite", dbAddress) | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if errWithCode, ok := err.(*sqlite.Error); ok { | 
					
						
							|  |  |  | 			err = errors.New(sqlite.ErrorCodeString[errWithCode.Code()]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("could not open sqlite db: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tweakConnectionValues(sqldb) | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 11:21:36 +02:00
										 |  |  | 	if inMem { | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 		// don't close connections on disconnect -- otherwise | 
					
						
							|  |  |  | 		// the SQLite database will be deleted when there | 
					
						
							|  |  |  | 		// are no active connections | 
					
						
							|  |  |  | 		sqldb.SetConnMaxLifetime(0) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	conn := WrapDBConn(bun.NewDB(sqldb, sqlitedialect.New())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ping to check the db is there and listening | 
					
						
							|  |  |  | 	if err := conn.PingContext(ctx); err != nil { | 
					
						
							|  |  |  | 		if errWithCode, ok := err.(*sqlite.Error); ok { | 
					
						
							|  |  |  | 			err = errors.New(sqlite.ErrorCodeString[errWithCode.Code()]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("sqlite ping: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	log.Info("connected to SQLITE database") | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	return conn, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | func pgConn(ctx context.Context) (*DBConn, error) { | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | 	opts, err := deriveBunDBPGOptions() //nolint:contextcheck | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("could not create bundb postgres options: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	sqldb := stdlib.OpenDB(*opts) | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	tweakConnectionValues(sqldb) | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	conn := WrapDBConn(bun.NewDB(sqldb, pgdialect.New())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ping to check the db is there and listening | 
					
						
							|  |  |  | 	if err := conn.PingContext(ctx); err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("postgres ping: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	log.Info("connected to POSTGRES database") | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 	return conn, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | /* | 
					
						
							|  |  |  | 	HANDY STUFF | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | // deriveBunDBPGOptions takes an application config and returns either a ready-to-use set of options | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | // with sensible defaults, or an error if it's not satisfied by the provided config. | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | func deriveBunDBPGOptions() (*pgx.ConnConfig, error) { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	if strings.ToUpper(config.GetDbType()) != db.DBTypePostgres { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("expected db type of %s but got %s", db.DBTypePostgres, config.DbTypeFlag()) | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-21 12:08:27 +01:00
										 |  |  | 	// these are all optional, the db adapter figures out defaults | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	address := config.GetDbAddress() | 
					
						
							| 
									
										
										
										
											2021-03-02 22:52:31 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// validate database | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	database := config.GetDbDatabase() | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	if database == "" { | 
					
						
							| 
									
										
										
										
											2021-03-04 12:07:24 +01:00
										 |  |  | 		return nil, errors.New("no database set") | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 	var tlsConfig *tls.Config | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	switch config.GetDbTLSMode() { | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	case dbTLSModeDisable, dbTLSModeUnset: | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		break // nothing to do | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	case dbTLSModeEnable: | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 		/* #nosec G402 */ | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		tlsConfig = &tls.Config{ | 
					
						
							|  |  |  | 			InsecureSkipVerify: true, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	case dbTLSModeRequire: | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		tlsConfig = &tls.Config{ | 
					
						
							|  |  |  | 			InsecureSkipVerify: false, | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 			ServerName:         address, | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 			MinVersion:         tls.VersionTLS12, | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	if certPath := config.GetDbTLSCACert(); tlsConfig != nil && certPath != "" { | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		// load the system cert pool first -- we'll append the given CA cert to this | 
					
						
							|  |  |  | 		certPool, err := x509.SystemCertPool() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("error fetching system CA cert pool: %s", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// open the file itself and make sure there's something in it | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 		caCertBytes, err := os.ReadFile(certPath) | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 			return nil, fmt.Errorf("error opening CA certificate at %s: %s", certPath, err) | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if len(caCertBytes) == 0 { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 			return nil, fmt.Errorf("ca cert at %s was empty", certPath) | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// make sure we have a PEM block | 
					
						
							|  |  |  | 		caPem, _ := pem.Decode(caCertBytes) | 
					
						
							|  |  |  | 		if caPem == nil { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 			return nil, fmt.Errorf("could not parse cert at %s into PEM", certPath) | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// parse the PEM block into the certificate | 
					
						
							|  |  |  | 		caCert, err := x509.ParseCertificate(caPem.Bytes) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 			return nil, fmt.Errorf("could not parse cert at %s into x509 certificate: %s", certPath, err) | 
					
						
							| 
									
										
										
										
											2021-07-19 18:03:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// we're happy, add it to the existing pool and then use this pool in our tls config | 
					
						
							|  |  |  | 		certPool.AddCert(caCert) | 
					
						
							|  |  |  | 		tlsConfig.RootCAs = certPool | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	cfg, _ := pgx.ParseConfig("") | 
					
						
							| 
									
										
										
										
											2021-12-21 12:08:27 +01:00
										 |  |  | 	if address != "" { | 
					
						
							|  |  |  | 		cfg.Host = address | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-03 15:40:38 +02:00
										 |  |  | 	if port := config.GetDbPort(); port > 0 { | 
					
						
							| 
									
										
										
										
											2021-12-21 12:08:27 +01:00
										 |  |  | 		cfg.Port = uint16(port) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	if u := config.GetDbUser(); u != "" { | 
					
						
							|  |  |  | 		cfg.User = u | 
					
						
							| 
									
										
										
										
											2021-12-21 12:08:27 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	if p := config.GetDbPassword(); p != "" { | 
					
						
							|  |  |  | 		cfg.Password = p | 
					
						
							| 
									
										
										
										
											2021-12-21 12:08:27 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if tlsConfig != nil { | 
					
						
							|  |  |  | 		cfg.TLSConfig = tlsConfig | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	cfg.Database = database | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	cfg.PreferSimpleProtocol = true | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	cfg.RuntimeParams["application_name"] = config.GetApplicationName() | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	return cfg, nil | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-20 18:20:21 +02:00
										 |  |  | // https://bun.uptrace.dev/postgres/running-bun-in-production.html#database-sql | 
					
						
							|  |  |  | func tweakConnectionValues(sqldb *sql.DB) { | 
					
						
							|  |  |  | 	maxOpenConns := 4 * runtime.GOMAXPROCS(0) | 
					
						
							|  |  |  | 	sqldb.SetMaxOpenConns(maxOpenConns) | 
					
						
							|  |  |  | 	sqldb.SetMaxIdleConns(maxOpenConns) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | /* | 
					
						
							|  |  |  | 	CONVERSION FUNCTIONS | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-26 11:56:01 +02:00
										 |  |  | func (dbService *DBService) TagStringsToTags(ctx context.Context, tags []string, originAccountID string) ([]*gtsmodel.Tag, error) { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	protocol := config.GetProtocol() | 
					
						
							|  |  |  | 	host := config.GetHost() | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	newTags := []*gtsmodel.Tag{} | 
					
						
							|  |  |  | 	for _, t := range tags { | 
					
						
							|  |  |  | 		tag := >smodel.Tag{} | 
					
						
							|  |  |  | 		// we can use selectorinsert here to create the new tag if it doesn't exist already | 
					
						
							|  |  |  | 		// inserted will be true if this is a new tag we just created | 
					
						
							| 
									
										
										
										
											2022-09-26 11:56:01 +02:00
										 |  |  | 		if err := dbService.conn.NewSelect().Model(tag).Where("LOWER(?) = LOWER(?)", bun.Ident("name"), t).Scan(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 			if err == sql.ErrNoRows { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 				// tag doesn't exist yet so populate it | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 				newID, err := id.NewRandomULID() | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return nil, err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				tag.ID = newID | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 				tag.URL = fmt.Sprintf("%s://%s/tags/%s", protocol, host, t) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 				tag.Name = t | 
					
						
							|  |  |  | 				tag.FirstSeenFromAccountID = originAccountID | 
					
						
							|  |  |  | 				tag.CreatedAt = time.Now() | 
					
						
							|  |  |  | 				tag.UpdatedAt = time.Now() | 
					
						
							| 
									
										
										
										
											2022-08-15 12:35:05 +02:00
										 |  |  | 				useable := true | 
					
						
							|  |  |  | 				tag.Useable = &useable | 
					
						
							|  |  |  | 				listable := true | 
					
						
							|  |  |  | 				tag.Listable = &listable | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				return nil, fmt.Errorf("error getting tag with name %s: %s", t, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// bail already if the tag isn't useable | 
					
						
							| 
									
										
										
										
											2022-08-15 12:35:05 +02:00
										 |  |  | 		if !*tag.Useable { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		tag.LastStatusAt = time.Now() | 
					
						
							|  |  |  | 		newTags = append(newTags, tag) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return newTags, nil | 
					
						
							|  |  |  | } |