| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	"github.com/ReneKroon/ttlcache" | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"github.com/jackc/pgx/v4" | 
					
						
							|  |  |  | 	"github.com/jackc/pgx/v4/stdlib" | 
					
						
							| 
									
										
										
										
											2021-03-02 22:52:31 +01:00
										 |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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{}{ | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	>smodel.StatusToEmoji{}, | 
					
						
							|  |  |  | 	>smodel.StatusToTag{}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | // bunDBService satisfies the DB interface | 
					
						
							|  |  |  | type bunDBService 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 | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	conn *DBConn | 
					
						
							| 
									
										
										
										
											2021-03-02 18:26:30 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-11 05:37:33 -07:00
										 |  |  | func doMigration(ctx context.Context, db *bun.DB) error { | 
					
						
							|  |  |  | 	l := logrus.WithField("func", "doMigration") | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 		l.Info("there are no new migrations to run") | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	l.Infof("MIGRATED DATABASE TO %s", group) | 
					
						
							|  |  |  | 	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-04-18 16:47:11 +02:00
										 |  |  | 	// add a hook to log queries and the time they take | 
					
						
							| 
									
										
										
										
											2022-03-07 11:33:18 +01:00
										 |  |  | 	// only do this for logging where performance isn't 1st concern | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	if logrus.GetLevel() >= logrus.DebugLevel && config.GetLogDbQueries() { | 
					
						
							| 
									
										
										
										
											2021-10-11 05:37:33 -07:00
										 |  |  | 		conn.DB.AddQueryHook(newDebugQueryHook()) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	accounts := &accountDB{conn: conn, cache: cache.NewAccountCache()} | 
					
						
							| 
									
										
										
										
											2021-09-01 11:13:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	ps := &bunDBService{ | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 		Account: accounts, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		Admin: &adminDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn: conn, | 
					
						
							| 
									
										
										
										
											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{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn: conn, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-05-20 04:34:36 -04:00
										 |  |  | 		Emoji: &emojiDB{ | 
					
						
							|  |  |  | 			conn: conn, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							|  |  |  | 			cache: ttlcache.NewCache(), | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Notification: ¬ificationDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn:  conn, | 
					
						
							|  |  |  | 			cache: ttlcache.NewCache(), | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Status: &statusDB{ | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 			conn:     conn, | 
					
						
							|  |  |  | 			cache:    cache.NewStatusCache(), | 
					
						
							|  |  |  | 			accounts: accounts, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Timeline: &timelineDB{ | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 			conn: conn, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 		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
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	if dbAddress == "file::memory:?cache=shared" { | 
					
						
							| 
									
										
										
										
											2021-11-21 17:41:51 +01:00
										 |  |  | 		logrus.Warn("sqlite in-memory database should only be used for debugging") | 
					
						
							|  |  |  | 		// 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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	logrus.Info("connected to SQLITE database") | 
					
						
							|  |  |  | 	return conn, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | func pgConn(ctx context.Context) (*DBConn, error) { | 
					
						
							|  |  |  | 	opts, err := deriveBunDBPGOptions() | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	logrus.Info("connected to POSTGRES database") | 
					
						
							|  |  |  | 	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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-11 13:19:06 +02:00
										 |  |  | func (ps *bunDBService) 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 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		if err := ps.conn.NewSelect().Model(tag).Where("LOWER(?) = LOWER(?)", bun.Ident("name"), t).Scan(ctx); err != nil { | 
					
						
							|  |  |  | 			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() | 
					
						
							|  |  |  | 				tag.Useable = true | 
					
						
							|  |  |  | 				tag.Listable = true | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				return nil, fmt.Errorf("error getting tag with name %s: %s", t, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// bail already if the tag isn't useable | 
					
						
							|  |  |  | 		if !tag.Useable { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		tag.LastStatusAt = time.Now() | 
					
						
							|  |  |  | 		newTags = append(newTags, tag) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return newTags, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-11 13:19:06 +02:00
										 |  |  | func (ps *bunDBService) EmojiStringsToEmojis(ctx context.Context, emojis []string) ([]*gtsmodel.Emoji, error) { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	newEmojis := []*gtsmodel.Emoji{} | 
					
						
							|  |  |  | 	for _, e := range emojis { | 
					
						
							|  |  |  | 		emoji := >smodel.Emoji{} | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		err := ps.conn.NewSelect().Model(emoji).Where("shortcode = ?", e).Where("visible_in_picker = true").Where("disabled = false").Scan(ctx) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 			if err == sql.ErrNoRows { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 				// no result found for this username/domain so just don't include it as an emoji and carry on about our business | 
					
						
							| 
									
										
										
										
											2021-10-11 05:37:33 -07:00
										 |  |  | 				logrus.Debugf("no emoji found with shortcode %s, skipping it", e) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// a serious error has happened so bail | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("error getting emoji with shortcode %s: %s", e, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		newEmojis = append(newEmojis, emoji) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return newEmojis, nil | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | } |