gotosocial/internal/db/bundb/admin.go

514 lines
14 KiB
Go
Raw Normal View History

// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package bundb
import (
"context"
"crypto/rand"
"crypto/rsa"
"errors"
"fmt"
"net/mail"
"strings"
"time"
"code.superseriousbusiness.org/gotosocial/internal/config"
"code.superseriousbusiness.org/gotosocial/internal/db"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
"code.superseriousbusiness.org/gotosocial/internal/id"
"code.superseriousbusiness.org/gotosocial/internal/log"
"code.superseriousbusiness.org/gotosocial/internal/state"
"code.superseriousbusiness.org/gotosocial/internal/uris"
"code.superseriousbusiness.org/gotosocial/internal/util"
"github.com/google/uuid"
"github.com/uptrace/bun"
"golang.org/x/crypto/bcrypt"
)
// generate RSA keys of this length
const rsaKeyBits = 2048
type adminDB struct {
db *bun.DB
state *state.State
}
2023-07-25 09:34:05 +01:00
func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, error) {
q := a.db.
NewSelect().
TableExpr("? AS ?", bun.Ident("accounts"), bun.Ident("account")).
Column("account.id").
Where("? = ?", bun.Ident("account.username"), username).
Where("? IS NULL", bun.Ident("account.domain"))
return notExists(ctx, q)
}
2023-07-25 09:34:05 +01:00
func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, error) {
// parse the domain from the email
m, err := mail.ParseAddress(email)
if err != nil {
return false, fmt.Errorf("error parsing email address %s: %s", email, err)
}
domain := strings.Split(m.Address, "@")[1] // domain will always be the second part after @
// check if the email domain is blocked
2023-07-25 09:34:05 +01:00
emailDomainBlockedQ := a.db.
NewSelect().
TableExpr("? AS ?", bun.Ident("email_domain_blocks"), bun.Ident("email_domain_block")).
Column("email_domain_block.id").
Where("? = ?", bun.Ident("email_domain_block.domain"), domain)
emailDomainBlocked, err := exists(ctx, emailDomainBlockedQ)
if err != nil {
return false, err
}
if emailDomainBlocked {
return false, fmt.Errorf("email domain %s is blocked", domain)
}
// check if this email is associated with a user already
2023-07-25 09:34:05 +01:00
q := a.db.
NewSelect().
TableExpr("? AS ?", bun.Ident("users"), bun.Ident("user")).
Column("user.id").
Where("? = ?", bun.Ident("user.email"), email).
WhereOr("? = ?", bun.Ident("user.unconfirmed_email"), email)
return notExists(ctx, q)
}
2023-07-25 09:34:05 +01:00
func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (*gtsmodel.User, error) {
// If something went wrong previously while doing a new
// sign up with this username, we might already have an
// account, so check first.
account, err := a.state.DB.GetAccountByUsernameDomain(ctx, newSignup.Username, "")
if err != nil && !errors.Is(err, db.ErrNoEntries) {
// Real error occurred.
err := gtserror.Newf("error checking for existing account: %w", err)
return nil, err
}
// If we didn't yet have an account
// with this username, create one now.
if account == nil {
uris := uris.GenerateURIsForAccount(newSignup.Username)
accountID, err := id.NewRandomULID()
if err != nil {
err := gtserror.Newf("error creating new account id: %w", err)
return nil, err
}
privKey, err := rsa.GenerateKey(rand.Reader, rsaKeyBits)
if err != nil {
err := gtserror.Newf("error creating new rsa private key: %w", err)
return nil, err
}
account = &gtsmodel.Account{
[feature] Use `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb` properties for web visibility of statuses (#4315) This pull request implements two new properties on ActivityPub actors: `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb`. As documented, these properties allow actors to signal their preference for whether or not their posts should be hidden from unauthenticated web views (ie., web pages like the GtS frontend, web apps like the Mastodon frontend, web APIs like the Mastodon public timeline API, etc). This allows remote accounts to *opt in* to having their unlisted visibility posts shown in (for example) the replies section of the web view of a GtS thread. In future, we can also use these properties to determine whether we should show boosts of a remote actor's post on a GtS profile, and that sort of thing. In keeping with our stance around privacy by default, GtS assumes `true` for `hidesCcPublicFromUnauthedWeb` if the property is not set on a remote actor, ie., hide unlisted/unlocked posts by default. `hidesToPublicFromUnauthedWeb` is assumed to be `false` if the property is not set on a remote actor, ie., show public posts by default. ~~WIP as I still want to work on the documentation for this a bit.~~ New props are already in the namespace document: https://gotosocial.org/ns Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4315 Reviewed-by: kim <gruf@noreply.codeberg.org> Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-07-09 16:50:25 +02:00
ID: accountID,
Username: newSignup.Username,
DisplayName: newSignup.Username,
URI: uris.UserURI,
URL: uris.UserURL,
InboxURI: uris.InboxURI,
OutboxURI: uris.OutboxURI,
FollowingURI: uris.FollowingURI,
FollowersURI: uris.FollowersURI,
FeaturedCollectionURI: uris.FeaturedCollectionURI,
ActorType: gtsmodel.AccountActorTypePerson,
PrivateKey: privKey,
PublicKey: &privKey.PublicKey,
PublicKeyURI: uris.PublicKeyURI,
HidesCcPublicFromUnauthedWeb: util.Ptr(true), // GtS default to hide unlisted.
}
// Insert the new account!
if err := a.state.DB.PutAccount(ctx, account); err != nil {
return nil, err
}
// Insert basic settings for new account.
account.Settings = &gtsmodel.AccountSettings{
AccountID: accountID,
Privacy: gtsmodel.VisibilityDefault,
}
if err := a.state.DB.PutAccountSettings(ctx, account.Settings); err != nil {
return nil, err
}
// Stub empty stats for new account.
if err := a.state.DB.StubAccountStats(ctx, account); err != nil {
return nil, err
}
}
// Created or already had an account.
// Ensure user not already created.
user, err := a.state.DB.GetUserByAccountID(ctx, account.ID)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
// Real error occurred.
err := gtserror.Newf("error checking for existing user: %w", err)
return nil, err
}
[bugfix] Fix nil ptr exception on creating user without running server first (#4094) # Description > If this is a code change, please include a summary of what you've coded, and link to the issue(s) it closes/implements. > > If this is a documentation change, please briefly describe what you've changed and why. closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4049 by removing the silly defer statement. Adds a test too, and a warning in the docs. ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [x] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4094 Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-04-29 16:40:36 +00:00
// If there's already a
// user for this account,
// just pin acct + return.
if user != nil {
[bugfix] Fix nil ptr exception on creating user without running server first (#4094) # Description > If this is a code change, please include a summary of what you've coded, and link to the issue(s) it closes/implements. > > If this is a documentation change, please briefly describe what you've changed and why. closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4049 by removing the silly defer statement. Adds a test too, and a warning in the docs. ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [x] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4094 Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-04-29 16:40:36 +00:00
user.Account = account
return user, nil
}
// Had no user for this account, time to create one!
newUserID, err := id.NewRandomULID()
if err != nil {
err := gtserror.Newf("error creating new user id: %w", err)
return nil, err
}
encryptedPassword, err := bcrypt.GenerateFromPassword(
[]byte(newSignup.Password),
bcrypt.DefaultCost,
)
if err != nil {
err := gtserror.Newf("error hashing password: %w", err)
return nil, err
}
// If no app ID was set,
[bugfix] Fix nil ptr exception on creating user without running server first (#4094) # Description > If this is a code change, please include a summary of what you've coded, and link to the issue(s) it closes/implements. > > If this is a documentation change, please briefly describe what you've changed and why. closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4049 by removing the silly defer statement. Adds a test too, and a warning in the docs. ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [x] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4094 Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-04-29 16:40:36 +00:00
// get the instance app
// and use its ID.
if newSignup.AppID == "" {
instanceApp, err := a.state.DB.GetInstanceApplication(ctx)
[bugfix] Fix nil ptr exception on creating user without running server first (#4094) # Description > If this is a code change, please include a summary of what you've coded, and link to the issue(s) it closes/implements. > > If this is a documentation change, please briefly describe what you've changed and why. closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4049 by removing the silly defer statement. Adds a test too, and a warning in the docs. ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [x] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4094 Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-04-29 16:40:36 +00:00
if err != nil && !errors.Is(err, db.ErrNoEntries) {
err := gtserror.Newf("db error getting instance app: %w", err)
return nil, err
}
[bugfix] Fix nil ptr exception on creating user without running server first (#4094) # Description > If this is a code change, please include a summary of what you've coded, and link to the issue(s) it closes/implements. > > If this is a documentation change, please briefly describe what you've changed and why. closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4049 by removing the silly defer statement. Adds a test too, and a warning in the docs. ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [x] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4094 Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-04-29 16:40:36 +00:00
if instanceApp == nil {
const errText = "instance application not yet created, run the server at least once *before* creating users"
err := gtserror.New(errText)
return nil, err
}
newSignup.AppID = instanceApp.ID
}
user = &gtsmodel.User{
ID: newUserID,
AccountID: account.ID,
Account: account,
EncryptedPassword: string(encryptedPassword),
SignUpIP: newSignup.SignUpIP.To4(),
Reason: newSignup.Reason,
Locale: newSignup.Locale,
UnconfirmedEmail: newSignup.Email,
CreatedByApplicationID: newSignup.AppID,
ExternalID: newSignup.ExternalID,
}
if newSignup.EmailVerified {
// Mark given email as confirmed.
user.ConfirmedAt = time.Now()
user.Email = newSignup.Email
}
if newSignup.Admin {
// Make new user mod + admin.
user.Moderator = util.Ptr(true)
user.Admin = util.Ptr(true)
}
if newSignup.PreApproved {
// Mark new user as approved.
user.Approved = util.Ptr(true)
}
// Insert the user!
if err := a.state.DB.PutUser(ctx, user); err != nil {
err := gtserror.Newf("db error inserting user: %w", err)
return nil, err
}
return user, nil
}
2023-07-25 09:34:05 +01:00
func (a *adminDB) CreateInstanceAccount(ctx context.Context) error {
[chore] Global server configuration overhaul (#575) * move config flag names and usage to config package, rewrite config package to use global Configuration{} struct Signed-off-by: kim <grufwub@gmail.com> * improved code comment Signed-off-by: kim <grufwub@gmail.com> * linter Signed-off-by: kim <grufwub@gmail.com> * fix unmarshaling Signed-off-by: kim <grufwub@gmail.com> * remove kim's custom go compiler changes Signed-off-by: kim <grufwub@gmail.com> * generate setter and flag-name functions, implement these in codebase Signed-off-by: kim <grufwub@gmail.com> * update deps Signed-off-by: kim <grufwub@gmail.com> * small change Signed-off-by: kim <grufwub@gmail.com> * appease the linter... Signed-off-by: kim <grufwub@gmail.com> * move configuration into ConfigState structure, ensure reloading to/from viper settings to keep in sync Signed-off-by: kim <grufwub@gmail.com> * lint Signed-off-by: kim <grufwub@gmail.com> * update code comments Signed-off-by: kim <grufwub@gmail.com> * fix merge issue Signed-off-by: kim <grufwub@gmail.com> * fix merge issue Signed-off-by: kim <grufwub@gmail.com> * improved version string (removes time + go version) Signed-off-by: kim <grufwub@gmail.com> * fix version string build to pass test script + consolidate logic in func Signed-off-by: kim <grufwub@gmail.com> * add license text, update config.Defaults comment Signed-off-by: kim <grufwub@gmail.com> * add license text to generated config helpers file Signed-off-by: kim <grufwub@gmail.com> * defer unlock on config.Set___(), to ensure unlocked on panic Signed-off-by: kim <grufwub@gmail.com> * make it more obvious which cmd flags are being attached Signed-off-by: kim <grufwub@gmail.com>
2022-05-30 13:41:24 +01:00
username := config.GetHost()
2023-07-25 09:34:05 +01:00
q := a.db.
NewSelect().
TableExpr("? AS ?", bun.Ident("accounts"), bun.Ident("account")).
Column("account.id").
Where("? = ?", bun.Ident("account.username"), username).
Where("? IS NULL", bun.Ident("account.domain"))
exists, err := exists(ctx, q)
if err != nil {
return err
}
if exists {
log.Infof(ctx, "instance account %s already exists", username)
return nil
}
key, err := rsa.GenerateKey(rand.Reader, rsaKeyBits)
if err != nil {
log.Errorf(ctx, "error creating new rsa key: %s", err)
return err
}
aID, err := id.NewRandomULID()
if err != nil {
return err
}
newAccountURIs := uris.GenerateURIsForAccount(username)
acct := &gtsmodel.Account{
ID: aID,
Username: username,
DisplayName: username,
URL: newAccountURIs.UserURL,
PrivateKey: key,
PublicKey: &key.PublicKey,
PublicKeyURI: newAccountURIs.PublicKeyURI,
ActorType: gtsmodel.AccountActorTypeService,
URI: newAccountURIs.UserURI,
InboxURI: newAccountURIs.InboxURI,
OutboxURI: newAccountURIs.OutboxURI,
FollowersURI: newAccountURIs.FollowersURI,
FollowingURI: newAccountURIs.FollowingURI,
FeaturedCollectionURI: newAccountURIs.FeaturedCollectionURI,
}
// insert the new account!
if err := a.state.DB.PutAccount(ctx, acct); err != nil {
return err
}
log.Infof(ctx, "instance account %s CREATED with id %s", username, acct.ID)
return nil
}
2023-07-25 09:34:05 +01:00
func (a *adminDB) CreateInstanceInstance(ctx context.Context) error {
[chore] Global server configuration overhaul (#575) * move config flag names and usage to config package, rewrite config package to use global Configuration{} struct Signed-off-by: kim <grufwub@gmail.com> * improved code comment Signed-off-by: kim <grufwub@gmail.com> * linter Signed-off-by: kim <grufwub@gmail.com> * fix unmarshaling Signed-off-by: kim <grufwub@gmail.com> * remove kim's custom go compiler changes Signed-off-by: kim <grufwub@gmail.com> * generate setter and flag-name functions, implement these in codebase Signed-off-by: kim <grufwub@gmail.com> * update deps Signed-off-by: kim <grufwub@gmail.com> * small change Signed-off-by: kim <grufwub@gmail.com> * appease the linter... Signed-off-by: kim <grufwub@gmail.com> * move configuration into ConfigState structure, ensure reloading to/from viper settings to keep in sync Signed-off-by: kim <grufwub@gmail.com> * lint Signed-off-by: kim <grufwub@gmail.com> * update code comments Signed-off-by: kim <grufwub@gmail.com> * fix merge issue Signed-off-by: kim <grufwub@gmail.com> * fix merge issue Signed-off-by: kim <grufwub@gmail.com> * improved version string (removes time + go version) Signed-off-by: kim <grufwub@gmail.com> * fix version string build to pass test script + consolidate logic in func Signed-off-by: kim <grufwub@gmail.com> * add license text, update config.Defaults comment Signed-off-by: kim <grufwub@gmail.com> * add license text to generated config helpers file Signed-off-by: kim <grufwub@gmail.com> * defer unlock on config.Set___(), to ensure unlocked on panic Signed-off-by: kim <grufwub@gmail.com> * make it more obvious which cmd flags are being attached Signed-off-by: kim <grufwub@gmail.com>
2022-05-30 13:41:24 +01:00
protocol := config.GetProtocol()
host := config.GetHost()
// check if instance entry already exists
2023-07-25 09:34:05 +01:00
q := a.db.
NewSelect().
Column("instance.id").
TableExpr("? AS ?", bun.Ident("instances"), bun.Ident("instance")).
Where("? = ?", bun.Ident("instance.domain"), host)
exists, err := exists(ctx, q)
if err != nil {
return err
}
if exists {
log.Infof(ctx, "instance entry already exists")
2021-08-26 17:46:24 +02:00
return nil
}
iID, err := id.NewRandomULID()
if err != nil {
return err
}
i := &gtsmodel.Instance{
ID: iID,
Domain: host,
Title: host,
URI: fmt.Sprintf("%s://%s", protocol, host),
}
2023-07-25 09:34:05 +01:00
insertQ := a.db.
NewInsert().
Model(i)
_, err = insertQ.Exec(ctx)
if err != nil {
return err
}
log.Infof(ctx, "created instance instance %s with id %s", host, i.ID)
return nil
}
func (a *adminDB) CreateInstanceApplication(ctx context.Context) error {
// Check if instance application already exists.
// Instance application client_id always = the
// instance account's ID so this is an easy check.
instanceAcct, err := a.state.DB.GetInstanceAccount(ctx, "")
if err != nil {
err := gtserror.Newf("db error getting instance account: %w", err)
return err
}
exists, err := exists(
ctx,
a.db.
NewSelect().
Column("application.id").
TableExpr("? AS ?", bun.Ident("applications"), bun.Ident("application")).
Where("? = ?", bun.Ident("application.client_id"), instanceAcct.ID),
)
if err != nil {
return err
}
if exists {
log.Infof(ctx, "instance application already exists")
return nil
}
// Generate new IDs for this
// application and its client.
protocol := config.GetProtocol()
host := config.GetHost()
url := protocol + "://" + host
clientID := instanceAcct.ID
clientSecret := uuid.NewString()
// Generate the application
// to put in the database.
app := &gtsmodel.Application{
ID: id.NewULID(),
Name: host + " instance application",
Website: url,
RedirectURIs: []string{url},
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: "write:accounts",
}
// Store it.
if err := a.state.DB.PutApplication(ctx, app); err != nil {
err := gtserror.Newf("db error storing instance application: %w", err)
return err
}
return nil
}
func (a *adminDB) GetInstanceApplication(ctx context.Context) (*gtsmodel.Application, error) {
// Instance app clientID == instanceAcct.ID,
// so get the instance account first.
instanceAcct, err := a.state.DB.GetInstanceAccount(ctx, "")
if err != nil {
return nil, err
}
app := new(gtsmodel.Application)
if err := a.db.
NewSelect().
Model(app).
Where("? = ?", bun.Ident("application.client_id"), instanceAcct.ID).
Scan(ctx); err != nil {
return nil, err
}
return app, nil
}
func (a *adminDB) CountApprovedSignupsSince(ctx context.Context, since time.Time) (int, error) {
return a.db.
NewSelect().
TableExpr("? AS ?", bun.Ident("users"), bun.Ident("user")).
Where("? > ?", bun.Ident("user.created_at"), since).
Where("? = ?", bun.Ident("user.approved"), true).
Count(ctx)
}
func (a *adminDB) CountUnhandledSignups(ctx context.Context) (int, error) {
return a.db.
NewSelect().
TableExpr("? AS ?", bun.Ident("users"), bun.Ident("user")).
// Approved is false by default.
// Explicitly rejected sign-ups end up elsewhere.
Where("? = ?", bun.Ident("user.approved"), false).
Count(ctx)
}
/*
ACTION FUNCS
*/
func (a *adminDB) GetAdminAction(ctx context.Context, id string) (*gtsmodel.AdminAction, error) {
action := new(gtsmodel.AdminAction)
if err := a.db.
NewSelect().
Model(action).
Scan(ctx); err != nil {
return nil, err
}
return action, nil
}
func (a *adminDB) GetAdminActions(ctx context.Context) ([]*gtsmodel.AdminAction, error) {
actions := make([]*gtsmodel.AdminAction, 0)
if err := a.db.
NewSelect().
Model(&actions).
Scan(ctx); err != nil {
return nil, err
}
return actions, nil
}
func (a *adminDB) PutAdminAction(ctx context.Context, action *gtsmodel.AdminAction) error {
_, err := a.db.
NewInsert().
Model(action).
Exec(ctx)
return err
}
func (a *adminDB) UpdateAdminAction(ctx context.Context, action *gtsmodel.AdminAction, columns ...string) error {
// Update the action's last-updated
action.UpdatedAt = time.Now()
if len(columns) != 0 {
columns = append(columns, "updated_at")
}
_, err := a.db.
NewUpdate().
Model(action).
Where("? = ?", bun.Ident("admin_action.id"), action.ID).
Column(columns...).
Exec(ctx)
return err
}
func (a *adminDB) DeleteAdminAction(ctx context.Context, id string) error {
_, err := a.db.
NewDelete().
TableExpr("? AS ?", bun.Ident("admin_actions"), bun.Ident("admin_action")).
Where("? = ?", bun.Ident("admin_action"), id).
Exec(ctx)
return err
}