diff options
Diffstat (limited to 'internal/cliactions')
-rw-r--r-- | internal/cliactions/action.go | 31 | ||||
-rw-r--r-- | internal/cliactions/admin/account/account.go | 210 | ||||
-rw-r--r-- | internal/cliactions/server/server.go | 179 |
3 files changed, 420 insertions, 0 deletions
diff --git a/internal/cliactions/action.go b/internal/cliactions/action.go new file mode 100644 index 000000000..3ba91de8d --- /dev/null +++ b/internal/cliactions/action.go @@ -0,0 +1,31 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package cliactions + +import ( + "context" + + "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/config" +) + +// GTSAction defines one *action* that can be taken by the gotosocial cli command. +// This can be either a long-running action (like server start) or something +// shorter like db init or db inspect. +type GTSAction func(context.Context, *config.Config, *logrus.Logger) error diff --git a/internal/cliactions/admin/account/account.go b/internal/cliactions/admin/account/account.go new file mode 100644 index 000000000..3bb1afada --- /dev/null +++ b/internal/cliactions/admin/account/account.go @@ -0,0 +1,210 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package account + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/cliactions" + "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/db/pg" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/util" +) + +// Create creates a new account in the database using the provided flags. +var Create cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error { + dbConn, err := pg.NewPostgresService(ctx, c, log) + if err != nil { + return fmt.Errorf("error creating dbservice: %s", err) + } + + username, ok := c.AccountCLIFlags[config.UsernameFlag] + if !ok { + return errors.New("no username set") + } + if err := util.ValidateUsername(username); err != nil { + return err + } + + email, ok := c.AccountCLIFlags[config.EmailFlag] + if !ok { + return errors.New("no email set") + } + if err := util.ValidateEmail(email); err != nil { + return err + } + + password, ok := c.AccountCLIFlags[config.PasswordFlag] + if !ok { + return errors.New("no password set") + } + if err := util.ValidateNewPassword(password); err != nil { + return err + } + + _, err = dbConn.NewSignup(username, "", false, email, password, nil, "", "") + if err != nil { + return err + } + + return dbConn.Stop(ctx) +} + +// Confirm sets a user to Approved, sets Email to the current UnconfirmedEmail value, and sets ConfirmedAt to now. +var Confirm cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error { + dbConn, err := pg.NewPostgresService(ctx, c, log) + if err != nil { + return fmt.Errorf("error creating dbservice: %s", err) + } + + username, ok := c.AccountCLIFlags[config.UsernameFlag] + if !ok { + return errors.New("no username set") + } + if err := util.ValidateUsername(username); err != nil { + return err + } + + a := >smodel.Account{} + if err := dbConn.GetLocalAccountByUsername(username, a); err != nil { + return err + } + + u := >smodel.User{} + if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { + return err + } + + u.Approved = true + u.Email = u.UnconfirmedEmail + u.ConfirmedAt = time.Now() + if err := dbConn.UpdateByID(u.ID, u); err != nil { + return err + } + + return dbConn.Stop(ctx) +} + +// Promote sets a user to admin. +var Promote cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error { + dbConn, err := pg.NewPostgresService(ctx, c, log) + if err != nil { + return fmt.Errorf("error creating dbservice: %s", err) + } + + username, ok := c.AccountCLIFlags[config.UsernameFlag] + if !ok { + return errors.New("no username set") + } + if err := util.ValidateUsername(username); err != nil { + return err + } + + a := >smodel.Account{} + if err := dbConn.GetLocalAccountByUsername(username, a); err != nil { + return err + } + + u := >smodel.User{} + if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { + return err + } + u.Admin = true + if err := dbConn.UpdateByID(u.ID, u); err != nil { + return err + } + + return dbConn.Stop(ctx) +} + +// Demote sets admin on a user to false. +var Demote cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error { + dbConn, err := pg.NewPostgresService(ctx, c, log) + if err != nil { + return fmt.Errorf("error creating dbservice: %s", err) + } + + username, ok := c.AccountCLIFlags[config.UsernameFlag] + if !ok { + return errors.New("no username set") + } + if err := util.ValidateUsername(username); err != nil { + return err + } + + a := >smodel.Account{} + if err := dbConn.GetLocalAccountByUsername(username, a); err != nil { + return err + } + + u := >smodel.User{} + if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { + return err + } + u.Admin = false + if err := dbConn.UpdateByID(u.ID, u); err != nil { + return err + } + + return dbConn.Stop(ctx) +} + +// Disable sets Disabled to true on a user. +var Disable cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error { + dbConn, err := pg.NewPostgresService(ctx, c, log) + if err != nil { + return fmt.Errorf("error creating dbservice: %s", err) + } + + username, ok := c.AccountCLIFlags[config.UsernameFlag] + if !ok { + return errors.New("no username set") + } + if err := util.ValidateUsername(username); err != nil { + return err + } + + a := >smodel.Account{} + if err := dbConn.GetLocalAccountByUsername(username, a); err != nil { + return err + } + + u := >smodel.User{} + if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { + return err + } + u.Disabled = true + if err := dbConn.UpdateByID(u.ID, u); err != nil { + return err + } + + return dbConn.Stop(ctx) +} + +// Suspend suspends the target account, cleanly removing all of its media, followers, following, likes, statuses, etc. +var Suspend cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error { + // TODO + return nil +} diff --git a/internal/cliactions/server/server.go b/internal/cliactions/server/server.go new file mode 100644 index 000000000..75337009a --- /dev/null +++ b/internal/cliactions/server/server.go @@ -0,0 +1,179 @@ +package server + +import ( + "context" + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + + "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" + "github.com/superseriousbusiness/gotosocial/internal/api/client/account" + "github.com/superseriousbusiness/gotosocial/internal/api/client/admin" + "github.com/superseriousbusiness/gotosocial/internal/api/client/app" + "github.com/superseriousbusiness/gotosocial/internal/api/client/auth" + "github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver" + "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest" + "github.com/superseriousbusiness/gotosocial/internal/api/client/instance" + mediaModule "github.com/superseriousbusiness/gotosocial/internal/api/client/media" + "github.com/superseriousbusiness/gotosocial/internal/api/client/notification" + "github.com/superseriousbusiness/gotosocial/internal/api/client/search" + "github.com/superseriousbusiness/gotosocial/internal/api/client/status" + "github.com/superseriousbusiness/gotosocial/internal/api/client/timeline" + "github.com/superseriousbusiness/gotosocial/internal/api/s2s/user" + "github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger" + "github.com/superseriousbusiness/gotosocial/internal/api/security" + "github.com/superseriousbusiness/gotosocial/internal/blob" + "github.com/superseriousbusiness/gotosocial/internal/cliactions" + "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/db/pg" + "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb" + "github.com/superseriousbusiness/gotosocial/internal/gotosocial" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/media" + "github.com/superseriousbusiness/gotosocial/internal/oauth" + "github.com/superseriousbusiness/gotosocial/internal/processing" + "github.com/superseriousbusiness/gotosocial/internal/router" + "github.com/superseriousbusiness/gotosocial/internal/transport" + "github.com/superseriousbusiness/gotosocial/internal/typeutils" +) + +var models []interface{} = []interface{}{ + >smodel.Account{}, + >smodel.Application{}, + >smodel.Block{}, + >smodel.DomainBlock{}, + >smodel.EmailDomainBlock{}, + >smodel.Follow{}, + >smodel.FollowRequest{}, + >smodel.MediaAttachment{}, + >smodel.Mention{}, + >smodel.Status{}, + >smodel.StatusFave{}, + >smodel.StatusBookmark{}, + >smodel.StatusMute{}, + >smodel.Tag{}, + >smodel.User{}, + >smodel.Emoji{}, + >smodel.Instance{}, + >smodel.Notification{}, + &oauth.Token{}, + &oauth.Client{}, +} + +// Start creates and starts a gotosocial server +var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error { + dbService, err := pg.NewPostgresService(ctx, c, log) + if err != nil { + return fmt.Errorf("error creating dbservice: %s", err) + } + + federatingDB := federatingdb.New(dbService, c, log) + + router, err := router.New(c, log) + if err != nil { + return fmt.Errorf("error creating router: %s", err) + } + + storageBackend, err := blob.NewLocal(c, log) + if err != nil { + return fmt.Errorf("error creating storage backend: %s", err) + } + + // build converters and util + typeConverter := typeutils.NewConverter(c, dbService) + + // build backend handlers + mediaHandler := media.New(c, dbService, storageBackend, log) + oauthServer := oauth.New(dbService, log) + transportController := transport.NewController(c, &federation.Clock{}, http.DefaultClient, log) + federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter) + processor := processing.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storageBackend, dbService, log) + if err := processor.Start(); err != nil { + return fmt.Errorf("error starting processor: %s", err) + } + + // build client api modules + authModule := auth.New(c, dbService, oauthServer, log) + accountModule := account.New(c, processor, log) + instanceModule := instance.New(c, processor, log) + appsModule := app.New(c, processor, log) + followRequestsModule := followrequest.New(c, processor, log) + webfingerModule := webfinger.New(c, processor, log) + usersModule := user.New(c, processor, log) + timelineModule := timeline.New(c, processor, log) + notificationModule := notification.New(c, processor, log) + searchModule := search.New(c, processor, log) + mm := mediaModule.New(c, processor, log) + fileServerModule := fileserver.New(c, processor, log) + adminModule := admin.New(c, processor, log) + statusModule := status.New(c, processor, log) + securityModule := security.New(c, log) + + apis := []api.ClientModule{ + // modules with middleware go first + securityModule, + authModule, + + // now everything else + accountModule, + instanceModule, + appsModule, + followRequestsModule, + mm, + fileServerModule, + adminModule, + statusModule, + webfingerModule, + usersModule, + timelineModule, + notificationModule, + searchModule, + } + + for _, m := range apis { + if err := m.Route(router); err != nil { + return fmt.Errorf("routing error: %s", err) + } + } + + for _, m := range models { + if err := dbService.CreateTable(m); err != nil { + return fmt.Errorf("table creation error: %s", err) + } + } + + if err := dbService.CreateInstanceAccount(); err != nil { + return fmt.Errorf("error creating instance account: %s", err) + } + + if err := dbService.CreateInstanceInstance(); err != nil { + return fmt.Errorf("error creating instance instance: %s", err) + } + + gts, err := gotosocial.NewServer(dbService, router, federator, c) + if err != nil { + return fmt.Errorf("error creating gotosocial service: %s", err) + } + + if err := gts.Start(ctx); err != nil { + return fmt.Errorf("error starting gotosocial service: %s", err) + } + + // catch shutdown signals from the operating system + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) + sig := <-sigs + log.Infof("received signal %s, shutting down", sig) + + // close down all running services in order + if err := gts.Stop(ctx); err != nil { + return fmt.Errorf("error closing gotosocial service: %s", err) + } + + log.Info("done! exiting...") + return nil +} |