move to ulid
This commit is contained in:
parent
625e6d654c
commit
26ee190338
1
go.mod
1
go.mod
@ -33,6 +33,7 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
|
github.com/oklog/ulid v1.3.1
|
||||||
github.com/onsi/gomega v1.13.0 // indirect
|
github.com/onsi/gomega v1.13.0 // indirect
|
||||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -204,6 +204,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
|||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
|
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
|
||||||
|
@ -17,372 +17,3 @@
|
|||||||
// */
|
// */
|
||||||
|
|
||||||
package account_test
|
package account_test
|
||||||
|
|
||||||
// import (
|
|
||||||
// "bytes"
|
|
||||||
// "encoding/json"
|
|
||||||
// "fmt"
|
|
||||||
// "io"
|
|
||||||
// "io/ioutil"
|
|
||||||
// "mime/multipart"
|
|
||||||
// "net/http"
|
|
||||||
// "net/http/httptest"
|
|
||||||
// "os"
|
|
||||||
// "testing"
|
|
||||||
|
|
||||||
// "github.com/gin-gonic/gin"
|
|
||||||
// "github.com/google/uuid"
|
|
||||||
// "github.com/stretchr/testify/assert"
|
|
||||||
// "github.com/stretchr/testify/suite"
|
|
||||||
// "github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
|
||||||
// "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
|
||||||
// "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
|
||||||
// "github.com/superseriousbusiness/gotosocial/testrig"
|
|
||||||
|
|
||||||
// "github.com/superseriousbusiness/gotosocial/internal/oauth"
|
|
||||||
// "golang.org/x/crypto/bcrypt"
|
|
||||||
// )
|
|
||||||
|
|
||||||
// type AccountCreateTestSuite struct {
|
|
||||||
// AccountStandardTestSuite
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (suite *AccountCreateTestSuite) SetupSuite() {
|
|
||||||
// suite.testTokens = testrig.NewTestTokens()
|
|
||||||
// suite.testClients = testrig.NewTestClients()
|
|
||||||
// suite.testApplications = testrig.NewTestApplications()
|
|
||||||
// suite.testUsers = testrig.NewTestUsers()
|
|
||||||
// suite.testAccounts = testrig.NewTestAccounts()
|
|
||||||
// suite.testAttachments = testrig.NewTestAttachments()
|
|
||||||
// suite.testStatuses = testrig.NewTestStatuses()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (suite *AccountCreateTestSuite) SetupTest() {
|
|
||||||
// suite.config = testrig.NewTestConfig()
|
|
||||||
// suite.db = testrig.NewTestDB()
|
|
||||||
// suite.storage = testrig.NewTestStorage()
|
|
||||||
// suite.log = testrig.NewTestLog()
|
|
||||||
// suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)))
|
|
||||||
// suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
|
|
||||||
// suite.accountModule = account.New(suite.config, suite.processor, suite.log).(*account.Module)
|
|
||||||
// testrig.StandardDBSetup(suite.db)
|
|
||||||
// testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (suite *AccountCreateTestSuite) TearDownTest() {
|
|
||||||
// testrig.StandardDBTeardown(suite.db)
|
|
||||||
// testrig.StandardStorageTeardown(suite.storage)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TestAccountCreatePOSTHandlerSuccessful checks the happy path for an account creation request: all the fields provided are valid,
|
|
||||||
// // and at the end of it a new user and account should be added into the database.
|
|
||||||
// //
|
|
||||||
// // This is the handler served at /api/v1/accounts as POST
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerSuccessful() {
|
|
||||||
|
|
||||||
// t := suite.testTokens["local_account_1"]
|
|
||||||
// oauthToken := oauth.TokenToOauthToken(t)
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", account.BasePath), nil) // the endpoint we're hitting
|
|
||||||
// ctx.Request.Form = suite.newUserFormHappyPath
|
|
||||||
// suite.accountModule.AccountCreatePOSTHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
|
|
||||||
// // 1. we should have OK from our call to the function
|
|
||||||
// suite.EqualValues(http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have a token in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// t := &model.Token{}
|
|
||||||
// err = json.Unmarshal(b, t)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.Equal(suite.T(), "we're authorized now!", t.AccessToken)
|
|
||||||
|
|
||||||
// // check new account
|
|
||||||
|
|
||||||
// // 1. we should be able to get the new account from the db
|
|
||||||
// acct := >smodel.Account{}
|
|
||||||
// err = suite.db.GetLocalAccountByUsername("test_user", acct)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.NotNil(suite.T(), acct)
|
|
||||||
// // 2. reason should be set
|
|
||||||
// assert.Equal(suite.T(), suite.newUserFormHappyPath.Get("reason"), acct.Reason)
|
|
||||||
// // 3. display name should be equal to username by default
|
|
||||||
// assert.Equal(suite.T(), suite.newUserFormHappyPath.Get("username"), acct.DisplayName)
|
|
||||||
// // 4. domain should be nil because this is a local account
|
|
||||||
// assert.Nil(suite.T(), nil, acct.Domain)
|
|
||||||
// // 5. id should be set and parseable as a uuid
|
|
||||||
// assert.NotNil(suite.T(), acct.ID)
|
|
||||||
// _, err = uuid.Parse(acct.ID)
|
|
||||||
// assert.Nil(suite.T(), err)
|
|
||||||
// // 6. private and public key should be set
|
|
||||||
// assert.NotNil(suite.T(), acct.PrivateKey)
|
|
||||||
// assert.NotNil(suite.T(), acct.PublicKey)
|
|
||||||
|
|
||||||
// // check new user
|
|
||||||
|
|
||||||
// // 1. we should be able to get the new user from the db
|
|
||||||
// usr := >smodel.User{}
|
|
||||||
// err = suite.db.GetWhere("unconfirmed_email", suite.newUserFormHappyPath.Get("email"), usr)
|
|
||||||
// assert.Nil(suite.T(), err)
|
|
||||||
// assert.NotNil(suite.T(), usr)
|
|
||||||
|
|
||||||
// // 2. user should have account id set to account we got above
|
|
||||||
// assert.Equal(suite.T(), acct.ID, usr.AccountID)
|
|
||||||
|
|
||||||
// // 3. id should be set and parseable as a uuid
|
|
||||||
// assert.NotNil(suite.T(), usr.ID)
|
|
||||||
// _, err = uuid.Parse(usr.ID)
|
|
||||||
// assert.Nil(suite.T(), err)
|
|
||||||
|
|
||||||
// // 4. locale should be equal to what we requested
|
|
||||||
// assert.Equal(suite.T(), suite.newUserFormHappyPath.Get("locale"), usr.Locale)
|
|
||||||
|
|
||||||
// // 5. created by application id should be equal to the app id
|
|
||||||
// assert.Equal(suite.T(), suite.testApplication.ID, usr.CreatedByApplicationID)
|
|
||||||
|
|
||||||
// // 6. password should be matcheable to what we set above
|
|
||||||
// err = bcrypt.CompareHashAndPassword([]byte(usr.EncryptedPassword), []byte(suite.newUserFormHappyPath.Get("password")))
|
|
||||||
// assert.Nil(suite.T(), err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TestAccountCreatePOSTHandlerNoAuth makes sure that the handler fails when no authorization is provided:
|
|
||||||
// // only registered applications can create accounts, and we don't provide one here.
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerNoAuth() {
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", account.BasePath), nil) // the endpoint we're hitting
|
|
||||||
// ctx.Request.Form = suite.newUserFormHappyPath
|
|
||||||
// suite.accountModule.AccountCreatePOSTHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
|
|
||||||
// // 1. we should have forbidden from our call to the function because we didn't auth
|
|
||||||
// suite.EqualValues(http.StatusForbidden, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have an error message in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.Equal(suite.T(), `{"error":"not authorized"}`, string(b))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TestAccountCreatePOSTHandlerNoAuth makes sure that the handler fails when no form is provided at all.
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerNoForm() {
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplication)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedToken, suite.testToken)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", account.BasePath), nil) // the endpoint we're hitting
|
|
||||||
// suite.accountModule.AccountCreatePOSTHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
// suite.EqualValues(http.StatusBadRequest, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have an error message in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.Equal(suite.T(), `{"error":"missing one or more required form values"}`, string(b))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TestAccountCreatePOSTHandlerWeakPassword makes sure that the handler fails when a weak password is provided
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerWeakPassword() {
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplication)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedToken, suite.testToken)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", account.BasePath), nil) // the endpoint we're hitting
|
|
||||||
// ctx.Request.Form = suite.newUserFormHappyPath
|
|
||||||
// // set a weak password
|
|
||||||
// ctx.Request.Form.Set("password", "weak")
|
|
||||||
// suite.accountModule.AccountCreatePOSTHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
// suite.EqualValues(http.StatusBadRequest, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have an error message in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.Equal(suite.T(), `{"error":"insecure password, try including more special characters, using uppercase letters, using numbers or using a longer password"}`, string(b))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TestAccountCreatePOSTHandlerWeirdLocale makes sure that the handler fails when a weird locale is provided
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerWeirdLocale() {
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplication)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedToken, suite.testToken)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", account.BasePath), nil) // the endpoint we're hitting
|
|
||||||
// ctx.Request.Form = suite.newUserFormHappyPath
|
|
||||||
// // set an invalid locale
|
|
||||||
// ctx.Request.Form.Set("locale", "neverneverland")
|
|
||||||
// suite.accountModule.AccountCreatePOSTHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
// suite.EqualValues(http.StatusBadRequest, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have an error message in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.Equal(suite.T(), `{"error":"language: tag is not well-formed"}`, string(b))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TestAccountCreatePOSTHandlerRegistrationsClosed makes sure that the handler fails when registrations are closed
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerRegistrationsClosed() {
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplication)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedToken, suite.testToken)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", account.BasePath), nil) // the endpoint we're hitting
|
|
||||||
// ctx.Request.Form = suite.newUserFormHappyPath
|
|
||||||
|
|
||||||
// // close registrations
|
|
||||||
// suite.config.AccountsConfig.OpenRegistration = false
|
|
||||||
// suite.accountModule.AccountCreatePOSTHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
// suite.EqualValues(http.StatusBadRequest, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have an error message in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.Equal(suite.T(), `{"error":"registration is not open for this server"}`, string(b))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TestAccountCreatePOSTHandlerReasonNotProvided makes sure that the handler fails when no reason is provided but one is required
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerReasonNotProvided() {
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplication)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedToken, suite.testToken)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", account.BasePath), nil) // the endpoint we're hitting
|
|
||||||
// ctx.Request.Form = suite.newUserFormHappyPath
|
|
||||||
|
|
||||||
// // remove reason
|
|
||||||
// ctx.Request.Form.Set("reason", "")
|
|
||||||
|
|
||||||
// suite.accountModule.AccountCreatePOSTHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
// suite.EqualValues(http.StatusBadRequest, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have an error message in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.Equal(suite.T(), `{"error":"no reason provided"}`, string(b))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TestAccountCreatePOSTHandlerReasonNotProvided makes sure that the handler fails when a crappy reason is presented but a good one is required
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerInsufficientReason() {
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplication)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedToken, suite.testToken)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", account.BasePath), nil) // the endpoint we're hitting
|
|
||||||
// ctx.Request.Form = suite.newUserFormHappyPath
|
|
||||||
|
|
||||||
// // remove reason
|
|
||||||
// ctx.Request.Form.Set("reason", "just cuz")
|
|
||||||
|
|
||||||
// suite.accountModule.AccountCreatePOSTHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
// suite.EqualValues(http.StatusBadRequest, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have an error message in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// assert.Equal(suite.T(), `{"error":"reason should be at least 40 chars but 'just cuz' was 8"}`, string(b))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /*
|
|
||||||
// TESTING: AccountUpdateCredentialsPATCHHandler
|
|
||||||
// */
|
|
||||||
|
|
||||||
// func (suite *AccountCreateTestSuite) TestAccountUpdateCredentialsPATCHHandler() {
|
|
||||||
|
|
||||||
// // put test local account in db
|
|
||||||
// err := suite.db.Put(suite.testAccountLocal)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
|
|
||||||
// // attach avatar to request
|
|
||||||
// aviFile, err := os.Open("../../media/test/test-jpeg.jpg")
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
// body := &bytes.Buffer{}
|
|
||||||
// writer := multipart.NewWriter(body)
|
|
||||||
|
|
||||||
// part, err := writer.CreateFormFile("avatar", "test-jpeg.jpg")
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
|
|
||||||
// _, err = io.Copy(part, aviFile)
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
|
|
||||||
// err = aviFile.Close()
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
|
|
||||||
// err = writer.Close()
|
|
||||||
// assert.NoError(suite.T(), err)
|
|
||||||
|
|
||||||
// // setup
|
|
||||||
// recorder := httptest.NewRecorder()
|
|
||||||
// ctx, _ := gin.CreateTestContext(recorder)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccountLocal)
|
|
||||||
// ctx.Set(oauth.SessionAuthorizedToken, suite.testToken)
|
|
||||||
// ctx.Request = httptest.NewRequest(http.MethodPatch, fmt.Sprintf("http://localhost:8080/%s", account.UpdateCredentialsPath), body) // the endpoint we're hitting
|
|
||||||
// ctx.Request.Header.Set("Content-Type", writer.FormDataContentType())
|
|
||||||
// suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
|
||||||
|
|
||||||
// // check response
|
|
||||||
|
|
||||||
// // 1. we should have OK because our request was valid
|
|
||||||
// suite.EqualValues(http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
// // 2. we should have an error message in the result body
|
|
||||||
// result := recorder.Result()
|
|
||||||
// defer result.Body.Close()
|
|
||||||
// // TODO: implement proper checks here
|
|
||||||
// //
|
|
||||||
// // b, err := ioutil.ReadAll(result.Body)
|
|
||||||
// // assert.NoError(suite.T(), err)
|
|
||||||
// // assert.Equal(suite.T(), `{"error":"not authorized"}`, string(b))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestAccountCreateTestSuite(t *testing.T) {
|
|
||||||
// suite.Run(t, new(AccountCreateTestSuite))
|
|
||||||
// }
|
|
||||||
|
@ -74,7 +74,7 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
|
|||||||
|
|
||||||
// ValidatePassword takes an email address and a password.
|
// ValidatePassword takes an email address and a password.
|
||||||
// The goal is to authenticate the password against the one for that email
|
// The goal is to authenticate the password against the one for that email
|
||||||
// address stored in the database. If OK, we return the userid (a uuid) for that user,
|
// address stored in the database. If OK, we return the userid (a ulid) for that user,
|
||||||
// so that it can be used in further Oauth flows to generate a token/retreieve an oauth client from the db.
|
// so that it can be used in further Oauth flows to generate a token/retreieve an oauth client from the db.
|
||||||
func (m *Module) ValidatePassword(email string, password string) (userid string, err error) {
|
func (m *Module) ValidatePassword(email string, password string) (userid string, err error) {
|
||||||
l := m.log.WithField("func", "ValidatePassword")
|
l := m.log.WithField("func", "ValidatePassword")
|
||||||
|
@ -32,7 +32,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// AccountIDKey is the url key for account id (an account uuid)
|
// AccountIDKey is the url key for account id (an account ulid)
|
||||||
AccountIDKey = "account_id"
|
AccountIDKey = "account_id"
|
||||||
// MediaTypeKey is the url key for media type (usually something like attachment or header etc)
|
// MediaTypeKey is the url key for media type (usually something like attachment or header etc)
|
||||||
MediaTypeKey = "media_type"
|
MediaTypeKey = "media_type"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type StatusTimelineResponse struct {
|
type StatusTimelineResponse struct {
|
||||||
Statuses []*Status
|
Statuses []*Status
|
||||||
LinkHeader string
|
LinkHeader string
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,20 @@ var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log
|
|||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %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)
|
||||||
|
}
|
||||||
|
|
||||||
federatingDB := federatingdb.New(dbService, c, log)
|
federatingDB := federatingdb.New(dbService, c, log)
|
||||||
|
|
||||||
router, err := router.New(c, log)
|
router, err := router.New(c, log)
|
||||||
@ -151,20 +165,6 @@ var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
gts, err := gotosocial.NewServer(dbService, router, federator, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating gotosocial service: %s", err)
|
return fmt.Errorf("error creating gotosocial service: %s", err)
|
||||||
|
@ -33,11 +33,11 @@ import (
|
|||||||
"github.com/go-pg/pg/extra/pgdebug"
|
"github.com/go-pg/pg/extra/pgdebug"
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/go-pg/pg/v10"
|
||||||
"github.com/go-pg/pg/v10/orm"
|
"github.com/go-pg/pg/v10/orm"
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
@ -334,6 +334,7 @@ func (ps *postgresService) AcceptFollowRequest(originAccountID string, targetAcc
|
|||||||
|
|
||||||
// create a new follow to 'replace' the request with
|
// create a new follow to 'replace' the request with
|
||||||
follow := >smodel.Follow{
|
follow := >smodel.Follow{
|
||||||
|
ID: fr.ID,
|
||||||
AccountID: originAccountID,
|
AccountID: originAccountID,
|
||||||
TargetAccountID: targetAccountID,
|
TargetAccountID: targetAccountID,
|
||||||
URI: fr.URI,
|
URI: fr.URI,
|
||||||
@ -360,8 +361,14 @@ func (ps *postgresService) CreateInstanceAccount() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
newAccountURIs := util.GenerateURIsForAccount(username, ps.config.Protocol, ps.config.Host)
|
newAccountURIs := util.GenerateURIsForAccount(username, ps.config.Protocol, ps.config.Host)
|
||||||
a := >smodel.Account{
|
a := >smodel.Account{
|
||||||
|
ID: aID,
|
||||||
Username: ps.config.Host,
|
Username: ps.config.Host,
|
||||||
DisplayName: username,
|
DisplayName: username,
|
||||||
URL: newAccountURIs.UserURL,
|
URL: newAccountURIs.UserURL,
|
||||||
@ -389,7 +396,13 @@ func (ps *postgresService) CreateInstanceAccount() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ps *postgresService) CreateInstanceInstance() error {
|
func (ps *postgresService) CreateInstanceInstance() error {
|
||||||
|
iID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
i := >smodel.Instance{
|
i := >smodel.Instance{
|
||||||
|
ID: iID,
|
||||||
Domain: ps.config.Host,
|
Domain: ps.config.Host,
|
||||||
Title: ps.config.Host,
|
Title: ps.config.Host,
|
||||||
URI: fmt.Sprintf("%s://%s", ps.config.Protocol, ps.config.Host),
|
URI: fmt.Sprintf("%s://%s", ps.config.Protocol, ps.config.Host),
|
||||||
@ -600,8 +613,13 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr
|
|||||||
}
|
}
|
||||||
|
|
||||||
newAccountURIs := util.GenerateURIsForAccount(username, ps.config.Protocol, ps.config.Host)
|
newAccountURIs := util.GenerateURIsForAccount(username, ps.config.Protocol, ps.config.Host)
|
||||||
|
newAccountID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
a := >smodel.Account{
|
a := >smodel.Account{
|
||||||
|
ID: newAccountID,
|
||||||
Username: username,
|
Username: username,
|
||||||
DisplayName: username,
|
DisplayName: username,
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
@ -625,8 +643,15 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error hashing password: %s", err)
|
return nil, fmt.Errorf("error hashing password: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newUserID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
u := >smodel.User{
|
u := >smodel.User{
|
||||||
AccountID: a.ID,
|
ID: newUserID,
|
||||||
|
AccountID: newAccountID,
|
||||||
EncryptedPassword: string(pw),
|
EncryptedPassword: string(pw),
|
||||||
SignUpIP: signUpIP,
|
SignUpIP: signUpIP,
|
||||||
Locale: locale,
|
Locale: locale,
|
||||||
@ -1364,7 +1389,11 @@ func (ps *postgresService) TagStringsToTags(tags []string, originAccountID strin
|
|||||||
if err := ps.conn.Model(tag).Where("LOWER(?) = LOWER(?)", pg.Ident("name"), t).Select(); err != nil {
|
if err := ps.conn.Model(tag).Where("LOWER(?) = LOWER(?)", pg.Ident("name"), t).Select(); err != nil {
|
||||||
if err == pg.ErrNoRows {
|
if err == pg.ErrNoRows {
|
||||||
// tag doesn't exist yet so populate it
|
// tag doesn't exist yet so populate it
|
||||||
tag.ID = uuid.NewString()
|
newID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tag.ID = newID
|
||||||
tag.URL = fmt.Sprintf("%s://%s/tags/%s", ps.config.Protocol, ps.config.Host, t)
|
tag.URL = fmt.Sprintf("%s://%s/tags/%s", ps.config.Protocol, ps.config.Host, t)
|
||||||
tag.Name = t
|
tag.Name = t
|
||||||
tag.FirstSeenFromAccountID = originAccountID
|
tag.FirstSeenFromAccountID = originAccountID
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -99,6 +100,14 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error converting note to status: %s", err)
|
return fmt.Errorf("error converting note to status: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// id the status based on the time it was created
|
||||||
|
statusID, err := id.NewULIDFromTime(status.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
status.ID = statusID
|
||||||
|
|
||||||
if err := f.db.Put(status); err != nil {
|
if err := f.db.Put(status); err != nil {
|
||||||
if _, ok := err.(db.ErrAlreadyExists); ok {
|
if _, ok := err.(db.ErrAlreadyExists); ok {
|
||||||
// the status already exists in the database, which means we've already handled everything else,
|
// the status already exists in the database, which means we've already handled everything else,
|
||||||
@ -128,6 +137,12 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
|
|||||||
return fmt.Errorf("could not convert Follow to follow request: %s", err)
|
return fmt.Errorf("could not convert Follow to follow request: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newID, err := id.NewULIDFromTime(followRequest.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
followRequest.ID = newID
|
||||||
|
|
||||||
if err := f.db.Put(followRequest); err != nil {
|
if err := f.db.Put(followRequest); err != nil {
|
||||||
return fmt.Errorf("database error inserting follow request: %s", err)
|
return fmt.Errorf("database error inserting follow request: %s", err)
|
||||||
}
|
}
|
||||||
@ -149,6 +164,12 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
|
|||||||
return fmt.Errorf("could not convert Like to fave: %s", err)
|
return fmt.Errorf("could not convert Like to fave: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newID, err := id.NewULIDFromTime(fave.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fave.ID = newID
|
||||||
|
|
||||||
if err := f.db.Put(fave); err != nil {
|
if err := f.db.Put(fave); err != nil {
|
||||||
return fmt.Errorf("database error inserting fave: %s", err)
|
return fmt.Errorf("database error inserting fave: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,10 @@ import (
|
|||||||
|
|
||||||
"github.com/go-fed/activity/streams"
|
"github.com/go-fed/activity/streams"
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
"github.com/go-fed/activity/streams/vocab"
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ func sameActor(activityActor vocab.ActivityStreamsActorProperty, followActor voc
|
|||||||
//
|
//
|
||||||
// The go-fed library will handle setting the 'id' property on the
|
// The go-fed library will handle setting the 'id' property on the
|
||||||
// activity or object provided with the value returned.
|
// activity or object provided with the value returned.
|
||||||
func (f *federatingDB) NewID(c context.Context, t vocab.Type) (id *url.URL, err error) {
|
func (f *federatingDB) NewID(c context.Context, t vocab.Type) (idURL *url.URL, err error) {
|
||||||
l := f.log.WithFields(
|
l := f.log.WithFields(
|
||||||
logrus.Fields{
|
logrus.Fields{
|
||||||
"func": "NewID",
|
"func": "NewID",
|
||||||
@ -99,7 +99,11 @@ func (f *federatingDB) NewID(c context.Context, t vocab.Type) (id *url.URL, err
|
|||||||
if iter.IsIRI() {
|
if iter.IsIRI() {
|
||||||
actorAccount := >smodel.Account{}
|
actorAccount := >smodel.Account{}
|
||||||
if err := f.db.GetWhere([]db.Where{{Key: "uri", Value: iter.GetIRI().String()}}, actorAccount); err == nil { // if there's an error here, just use the fallback behavior -- we don't need to return an error here
|
if err := f.db.GetWhere([]db.Where{{Key: "uri", Value: iter.GetIRI().String()}}, actorAccount); err == nil { // if there's an error here, just use the fallback behavior -- we don't need to return an error here
|
||||||
return url.Parse(util.GenerateURIForFollow(actorAccount.Username, f.config.Protocol, f.config.Host, uuid.NewString()))
|
newID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return url.Parse(util.GenerateURIForFollow(actorAccount.Username, f.config.Protocol, f.config.Host, newID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,8 +162,12 @@ func (f *federatingDB) NewID(c context.Context, t vocab.Type) (id *url.URL, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback default behavior: just return a random UUID after our protocol and host
|
// fallback default behavior: just return a random ULID after our protocol and host
|
||||||
return url.Parse(fmt.Sprintf("%s://%s/%s", f.config.Protocol, f.config.Host, uuid.NewString()))
|
newID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return url.Parse(fmt.Sprintf("%s://%s/%s", f.config.Protocol, f.config.Host, newID))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActorForOutbox fetches the actor's IRI for the given outbox IRI.
|
// ActorForOutbox fetches the actor's IRI for the given outbox IRI.
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -142,6 +143,12 @@ func (f *federator) AuthenticatePostInbox(ctx context.Context, w http.ResponseWr
|
|||||||
return ctx, false, fmt.Errorf("error converting person with public key id %s to account: %s", publicKeyOwnerURI.String(), err)
|
return ctx, false, fmt.Errorf("error converting person with public key id %s to account: %s", publicKeyOwnerURI.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return ctx, false, err
|
||||||
|
}
|
||||||
|
a.ID = aID
|
||||||
|
|
||||||
if err := f.db.Put(a); err != nil {
|
if err := f.db.Put(a); err != nil {
|
||||||
l.Errorf("error inserting dereferenced remote account: %s", err)
|
l.Errorf("error inserting dereferenced remote account: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ type Account struct {
|
|||||||
BASIC INFO
|
BASIC INFO
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// id of this account in the local database; the end-user will never need to know this, it's strictly internal
|
// id of this account in the local database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// Username of the account, should just be a string of [a-z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``
|
// Username of the account, should just be a string of [a-z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``
|
||||||
Username string `pg:",notnull,unique:userdomain"` // username and domain should be unique *with* each other
|
Username string `pg:",notnull,unique:userdomain"` // username and domain should be unique *with* each other
|
||||||
// Domain of the account, will be null if this is a local account, otherwise something like ``example.org`` or ``mastodon.social``. Should be unique with username.
|
// Domain of the account, will be null if this is a local account, otherwise something like ``example.org`` or ``mastodon.social``. Should be unique with username.
|
||||||
@ -45,11 +45,11 @@ type Account struct {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// ID of the avatar as a media attachment
|
// ID of the avatar as a media attachment
|
||||||
AvatarMediaAttachmentID string
|
AvatarMediaAttachmentID string `pg:"type:CHAR(26)"`
|
||||||
// For a non-local account, where can the header be fetched?
|
// For a non-local account, where can the header be fetched?
|
||||||
AvatarRemoteURL string
|
AvatarRemoteURL string
|
||||||
// ID of the header as a media attachment
|
// ID of the header as a media attachment
|
||||||
HeaderMediaAttachmentID string
|
HeaderMediaAttachmentID string `pg:"type:CHAR(26)"`
|
||||||
// For a non-local account, where can the header be fetched?
|
// For a non-local account, where can the header be fetched?
|
||||||
HeaderRemoteURL string
|
HeaderRemoteURL string
|
||||||
// DisplayName for this account. Can be empty, then just the Username will be used for display purposes.
|
// DisplayName for this account. Can be empty, then just the Username will be used for display purposes.
|
||||||
@ -61,7 +61,7 @@ type Account struct {
|
|||||||
// Is this a memorial account, ie., has the user passed away?
|
// Is this a memorial account, ie., has the user passed away?
|
||||||
Memorial bool
|
Memorial bool
|
||||||
// This account has moved this account id in the database
|
// This account has moved this account id in the database
|
||||||
MovedToAccountID string
|
MovedToAccountID string `pg:"type:CHAR(26)"`
|
||||||
// When was this account created?
|
// When was this account created?
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// When was this account last updated?
|
// When was this account last updated?
|
||||||
|
@ -22,7 +22,7 @@ package gtsmodel
|
|||||||
// It is used to authorize tokens etc, and is associated with an oauth client id in the database.
|
// It is used to authorize tokens etc, and is associated with an oauth client id in the database.
|
||||||
type Application struct {
|
type Application struct {
|
||||||
// id of this application in the db
|
// id of this application in the db
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"`
|
ID string `pg:"type:CHAR(26),pk,notnull"`
|
||||||
// name of the application given when it was created (eg., 'tusky')
|
// name of the application given when it was created (eg., 'tusky')
|
||||||
Name string
|
Name string
|
||||||
// website for the application given when it was created (eg., 'https://tusky.app')
|
// website for the application given when it was created (eg., 'https://tusky.app')
|
||||||
@ -30,7 +30,7 @@ type Application struct {
|
|||||||
// redirect uri requested by the application for oauth2 flow
|
// redirect uri requested by the application for oauth2 flow
|
||||||
RedirectURI string
|
RedirectURI string
|
||||||
// id of the associated oauth client entity in the db
|
// id of the associated oauth client entity in the db
|
||||||
ClientID string
|
ClientID string `pg:"type:CHAR(26)"`
|
||||||
// secret of the associated oauth client entity in the db
|
// secret of the associated oauth client entity in the db
|
||||||
ClientSecret string
|
ClientSecret string
|
||||||
// scopes requested when this app was created
|
// scopes requested when this app was created
|
||||||
|
@ -5,15 +5,15 @@ import "time"
|
|||||||
// Block refers to the blocking of one account by another.
|
// Block refers to the blocking of one account by another.
|
||||||
type Block struct {
|
type Block struct {
|
||||||
// id of this block in the database
|
// id of this block in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"`
|
ID string `pg:"type:CHAR(26),pk,notnull"`
|
||||||
// When was this block created
|
// When was this block created
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// When was this block updated
|
// When was this block updated
|
||||||
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// Who created this block?
|
// Who created this block?
|
||||||
AccountID string `pg:",notnull"`
|
AccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// Who is targeted by this block?
|
// Who is targeted by this block?
|
||||||
TargetAccountID string `pg:",notnull"`
|
TargetAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// Activitypub URI for this block
|
// Activitypub URI for this block
|
||||||
URI string
|
URI string
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import "time"
|
|||||||
// DomainBlock represents a federation block against a particular domain, of varying severity.
|
// DomainBlock represents a federation block against a particular domain, of varying severity.
|
||||||
type DomainBlock struct {
|
type DomainBlock struct {
|
||||||
// ID of this block in the database
|
// ID of this block in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// Domain to block. If ANY PART of the candidate domain contains this string, it will be blocked.
|
// Domain to block. If ANY PART of the candidate domain contains this string, it will be blocked.
|
||||||
// For example: 'example.org' also blocks 'gts.example.org'. '.com' blocks *any* '.com' domains.
|
// For example: 'example.org' also blocks 'gts.example.org'. '.com' blocks *any* '.com' domains.
|
||||||
// TODO: implement wildcards here
|
// TODO: implement wildcards here
|
||||||
@ -33,7 +33,7 @@ type DomainBlock struct {
|
|||||||
// When was this block updated
|
// When was this block updated
|
||||||
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// Account ID of the creator of this block
|
// Account ID of the creator of this block
|
||||||
CreatedByAccountID string `pg:",notnull"`
|
CreatedByAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// TODO: define this
|
// TODO: define this
|
||||||
Severity int
|
Severity int
|
||||||
// Reject media from this domain?
|
// Reject media from this domain?
|
||||||
|
@ -23,7 +23,7 @@ import "time"
|
|||||||
// EmailDomainBlock represents a domain that the server should automatically reject sign-up requests from.
|
// EmailDomainBlock represents a domain that the server should automatically reject sign-up requests from.
|
||||||
type EmailDomainBlock struct {
|
type EmailDomainBlock struct {
|
||||||
// ID of this block in the database
|
// ID of this block in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// Email domain to block. Eg. 'gmail.com' or 'hotmail.com'
|
// Email domain to block. Eg. 'gmail.com' or 'hotmail.com'
|
||||||
Domain string `pg:",notnull"`
|
Domain string `pg:",notnull"`
|
||||||
// When was this block created
|
// When was this block created
|
||||||
@ -31,5 +31,5 @@ type EmailDomainBlock struct {
|
|||||||
// When was this block updated
|
// When was this block updated
|
||||||
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// Account ID of the creator of this block
|
// Account ID of the creator of this block
|
||||||
CreatedByAccountID string `pg:",notnull"`
|
CreatedByAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import "time"
|
|||||||
// Emoji represents a custom emoji that's been uploaded through the admin UI, and is useable by instance denizens.
|
// Emoji represents a custom emoji that's been uploaded through the admin UI, and is useable by instance denizens.
|
||||||
type Emoji struct {
|
type Emoji struct {
|
||||||
// database ID of this emoji
|
// database ID of this emoji
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"`
|
ID string `pg:"type:CHAR(26),pk,notnull"`
|
||||||
// String shortcode for this emoji -- the part that's between colons. This should be lowercase a-z_
|
// String shortcode for this emoji -- the part that's between colons. This should be lowercase a-z_
|
||||||
// eg., 'blob_hug' 'purple_heart' Must be unique with domain.
|
// eg., 'blob_hug' 'purple_heart' Must be unique with domain.
|
||||||
Shortcode string `pg:",notnull,unique:shortcodedomain"`
|
Shortcode string `pg:",notnull,unique:shortcodedomain"`
|
||||||
@ -73,5 +73,5 @@ type Emoji struct {
|
|||||||
// Is this emoji visible in the admin emoji picker?
|
// Is this emoji visible in the admin emoji picker?
|
||||||
VisibleInPicker bool `pg:",notnull,default:true"`
|
VisibleInPicker bool `pg:",notnull,default:true"`
|
||||||
// In which emoji category is this emoji visible?
|
// In which emoji category is this emoji visible?
|
||||||
CategoryID string
|
CategoryID string `pg:"type:CHAR(26)"`
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,15 @@ import "time"
|
|||||||
// Follow represents one account following another, and the metadata around that follow.
|
// Follow represents one account following another, and the metadata around that follow.
|
||||||
type Follow struct {
|
type Follow struct {
|
||||||
// id of this follow in the database
|
// id of this follow in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// When was this follow created?
|
// When was this follow created?
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// When was this follow last updated?
|
// When was this follow last updated?
|
||||||
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// Who does this follow belong to?
|
// Who does this follow belong to?
|
||||||
AccountID string `pg:",unique:srctarget,notnull"`
|
AccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"`
|
||||||
// Who does AccountID follow?
|
// Who does AccountID follow?
|
||||||
TargetAccountID string `pg:",unique:srctarget,notnull"`
|
TargetAccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"`
|
||||||
// Does this follow also want to see reblogs and not just posts?
|
// Does this follow also want to see reblogs and not just posts?
|
||||||
ShowReblogs bool `pg:"default:true"`
|
ShowReblogs bool `pg:"default:true"`
|
||||||
// What is the activitypub URI of this follow?
|
// What is the activitypub URI of this follow?
|
||||||
|
@ -23,15 +23,15 @@ import "time"
|
|||||||
// FollowRequest represents one account requesting to follow another, and the metadata around that request.
|
// FollowRequest represents one account requesting to follow another, and the metadata around that request.
|
||||||
type FollowRequest struct {
|
type FollowRequest struct {
|
||||||
// id of this follow request in the database
|
// id of this follow request in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// When was this follow request created?
|
// When was this follow request created?
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// When was this follow request last updated?
|
// When was this follow request last updated?
|
||||||
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// Who does this follow request originate from?
|
// Who does this follow request originate from?
|
||||||
AccountID string `pg:",unique:srctarget,notnull"`
|
AccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"`
|
||||||
// Who is the target of this follow request?
|
// Who is the target of this follow request?
|
||||||
TargetAccountID string `pg:",unique:srctarget,notnull"`
|
TargetAccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"`
|
||||||
// Does this follow also want to see reblogs and not just posts?
|
// Does this follow also want to see reblogs and not just posts?
|
||||||
ShowReblogs bool `pg:"default:true"`
|
ShowReblogs bool `pg:"default:true"`
|
||||||
// What is the activitypub URI of this follow request?
|
// What is the activitypub URI of this follow request?
|
||||||
|
@ -5,7 +5,7 @@ import "time"
|
|||||||
// Instance represents a federated instance, either local or remote.
|
// Instance represents a federated instance, either local or remote.
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
// ID of this instance in the database
|
// ID of this instance in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// Instance domain eg example.org
|
// Instance domain eg example.org
|
||||||
Domain string `pg:",notnull,unique"`
|
Domain string `pg:",notnull,unique"`
|
||||||
// Title of this instance as it would like to be displayed.
|
// Title of this instance as it would like to be displayed.
|
||||||
@ -19,7 +19,7 @@ type Instance struct {
|
|||||||
// When was this instance suspended, if at all?
|
// When was this instance suspended, if at all?
|
||||||
SuspendedAt time.Time
|
SuspendedAt time.Time
|
||||||
// ID of any existing domain block for this instance in the database
|
// ID of any existing domain block for this instance in the database
|
||||||
DomainBlockID string
|
DomainBlockID string `pg:"type:CHAR(26)"`
|
||||||
// Short description of this instance
|
// Short description of this instance
|
||||||
ShortDescription string
|
ShortDescription string
|
||||||
// Longer description of this instance
|
// Longer description of this instance
|
||||||
@ -27,7 +27,7 @@ type Instance struct {
|
|||||||
// Contact email address for this instance
|
// Contact email address for this instance
|
||||||
ContactEmail string
|
ContactEmail string
|
||||||
// Contact account ID in the database for this instance
|
// Contact account ID in the database for this instance
|
||||||
ContactAccountID string
|
ContactAccountID string `pg:"type:CHAR(26)"`
|
||||||
// Reputation score of this instance
|
// Reputation score of this instance
|
||||||
Reputation int64 `pg:",notnull,default:0"`
|
Reputation int64 `pg:",notnull,default:0"`
|
||||||
// Version of the software used on this instance
|
// Version of the software used on this instance
|
||||||
|
@ -26,9 +26,9 @@ import (
|
|||||||
// somewhere in storage and that can be retrieved and served by the router.
|
// somewhere in storage and that can be retrieved and served by the router.
|
||||||
type MediaAttachment struct {
|
type MediaAttachment struct {
|
||||||
// ID of the attachment in the database
|
// ID of the attachment in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// ID of the status to which this is attached
|
// ID of the status to which this is attached
|
||||||
StatusID string
|
StatusID string `pg:"type:CHAR(26)"`
|
||||||
// Where can the attachment be retrieved on *this* server
|
// Where can the attachment be retrieved on *this* server
|
||||||
URL string
|
URL string
|
||||||
// Where can the attachment be retrieved on a remote server (empty for local media)
|
// Where can the attachment be retrieved on a remote server (empty for local media)
|
||||||
@ -42,11 +42,11 @@ type MediaAttachment struct {
|
|||||||
// Metadata about the file
|
// Metadata about the file
|
||||||
FileMeta FileMeta
|
FileMeta FileMeta
|
||||||
// To which account does this attachment belong
|
// To which account does this attachment belong
|
||||||
AccountID string `pg:",notnull"`
|
AccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// Description of the attachment (for screenreaders)
|
// Description of the attachment (for screenreaders)
|
||||||
Description string
|
Description string
|
||||||
// To which scheduled status does this attachment belong
|
// To which scheduled status does this attachment belong
|
||||||
ScheduledStatusID string
|
ScheduledStatusID string `pg:"type:CHAR(26)"`
|
||||||
// What is the generated blurhash of this attachment
|
// What is the generated blurhash of this attachment
|
||||||
Blurhash string
|
Blurhash string
|
||||||
// What is the processing status of this attachment
|
// What is the processing status of this attachment
|
||||||
|
@ -23,19 +23,19 @@ import "time"
|
|||||||
// Mention refers to the 'tagging' or 'mention' of a user within a status.
|
// Mention refers to the 'tagging' or 'mention' of a user within a status.
|
||||||
type Mention struct {
|
type Mention struct {
|
||||||
// ID of this mention in the database
|
// ID of this mention in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// ID of the status this mention originates from
|
// ID of the status this mention originates from
|
||||||
StatusID string `pg:",notnull"`
|
StatusID string `pg:"type:CHAR(26),notnull"`
|
||||||
// When was this mention created?
|
// When was this mention created?
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// When was this mention last updated?
|
// When was this mention last updated?
|
||||||
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// What's the internal account ID of the originator of the mention?
|
// What's the internal account ID of the originator of the mention?
|
||||||
OriginAccountID string `pg:",notnull"`
|
OriginAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// What's the AP URI of the originator of the mention?
|
// What's the AP URI of the originator of the mention?
|
||||||
OriginAccountURI string `pg:",notnull"`
|
OriginAccountURI string `pg:",notnull"`
|
||||||
// What's the internal account ID of the mention target?
|
// What's the internal account ID of the mention target?
|
||||||
TargetAccountID string `pg:",notnull"`
|
TargetAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// Prevent this mention from generating a notification?
|
// Prevent this mention from generating a notification?
|
||||||
Silent bool
|
Silent bool
|
||||||
|
|
||||||
|
@ -23,17 +23,17 @@ import "time"
|
|||||||
// Notification models an alert/notification sent to an account about something like a reblog, like, new follow request, etc.
|
// Notification models an alert/notification sent to an account about something like a reblog, like, new follow request, etc.
|
||||||
type Notification struct {
|
type Notification struct {
|
||||||
// ID of this notification in the database
|
// ID of this notification in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"`
|
ID string `pg:"type:CHAR(26),pk,notnull"`
|
||||||
// Type of this notification
|
// Type of this notification
|
||||||
NotificationType NotificationType `pg:",notnull"`
|
NotificationType NotificationType `pg:",notnull"`
|
||||||
// Creation time of this notification
|
// Creation time of this notification
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// Which account does this notification target (ie., who will receive the notification?)
|
// Which account does this notification target (ie., who will receive the notification?)
|
||||||
TargetAccountID string `pg:",notnull"`
|
TargetAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// Which account performed the action that created this notification?
|
// Which account performed the action that created this notification?
|
||||||
OriginAccountID string `pg:",notnull"`
|
OriginAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// If the notification pertains to a status, what is the database ID of that status?
|
// If the notification pertains to a status, what is the database ID of that status?
|
||||||
StatusID string
|
StatusID string `pg:"type:CHAR(26)"`
|
||||||
// Has this notification been read already?
|
// Has this notification been read already?
|
||||||
Read bool
|
Read bool
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import "time"
|
|||||||
// Status represents a user-created 'post' or 'status' in the database, either remote or local
|
// Status represents a user-created 'post' or 'status' in the database, either remote or local
|
||||||
type Status struct {
|
type Status struct {
|
||||||
// id of the status in the database
|
// id of the status in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"`
|
ID string `pg:"type:CHAR(26),pk,notnull"`
|
||||||
// uri at which this status is reachable
|
// uri at which this status is reachable
|
||||||
URI string `pg:",unique"`
|
URI string `pg:",unique"`
|
||||||
// web url for viewing this status
|
// web url for viewing this status
|
||||||
@ -45,13 +45,13 @@ type Status struct {
|
|||||||
// is this status from a local account?
|
// is this status from a local account?
|
||||||
Local bool
|
Local bool
|
||||||
// which account posted this status?
|
// which account posted this status?
|
||||||
AccountID string
|
AccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// id of the status this status is a reply to
|
// id of the status this status is a reply to
|
||||||
InReplyToID string
|
InReplyToID string `pg:"type:CHAR(26)"`
|
||||||
// id of the account that this status replies to
|
// id of the account that this status replies to
|
||||||
InReplyToAccountID string
|
InReplyToAccountID string `pg:"type:CHAR(26)"`
|
||||||
// id of the status this status is a boost of
|
// id of the status this status is a boost of
|
||||||
BoostOfID string
|
BoostOfID string `pg:"type:CHAR(26)"`
|
||||||
// cw string for this status
|
// cw string for this status
|
||||||
ContentWarning string
|
ContentWarning string
|
||||||
// visibility entry for this status
|
// visibility entry for this status
|
||||||
@ -61,7 +61,7 @@ type Status struct {
|
|||||||
// what language is this status written in?
|
// what language is this status written in?
|
||||||
Language string
|
Language string
|
||||||
// Which application was used to create this status?
|
// Which application was used to create this status?
|
||||||
CreatedWithApplicationID string
|
CreatedWithApplicationID string `pg:"type:CHAR(26)"`
|
||||||
// advanced visibility for this status
|
// advanced visibility for this status
|
||||||
VisibilityAdvanced *VisibilityAdvanced
|
VisibilityAdvanced *VisibilityAdvanced
|
||||||
// What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types
|
// What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types
|
||||||
|
@ -23,13 +23,13 @@ import "time"
|
|||||||
// StatusBookmark refers to one account having a 'bookmark' of the status of another account
|
// StatusBookmark refers to one account having a 'bookmark' of the status of another account
|
||||||
type StatusBookmark struct {
|
type StatusBookmark struct {
|
||||||
// id of this bookmark in the database
|
// id of this bookmark in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// when was this bookmark created
|
// when was this bookmark created
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// id of the account that created ('did') the bookmarking
|
// id of the account that created ('did') the bookmarking
|
||||||
AccountID string `pg:",notnull"`
|
AccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// id the account owning the bookmarked status
|
// id the account owning the bookmarked status
|
||||||
TargetAccountID string `pg:",notnull"`
|
TargetAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// database id of the status that has been bookmarked
|
// database id of the status that has been bookmarked
|
||||||
StatusID string `pg:",notnull"`
|
StatusID string `pg:"type:CHAR(26),notnull"`
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,15 @@ import "time"
|
|||||||
// StatusFave refers to a 'fave' or 'like' in the database, from one account, targeting the status of another account
|
// StatusFave refers to a 'fave' or 'like' in the database, from one account, targeting the status of another account
|
||||||
type StatusFave struct {
|
type StatusFave struct {
|
||||||
// id of this fave in the database
|
// id of this fave in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// when was this fave created
|
// when was this fave created
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// id of the account that created ('did') the fave
|
// id of the account that created ('did') the fave
|
||||||
AccountID string `pg:",notnull"`
|
AccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// id the account owning the faved status
|
// id the account owning the faved status
|
||||||
TargetAccountID string `pg:",notnull"`
|
TargetAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// database id of the status that has been 'faved'
|
// database id of the status that has been 'faved'
|
||||||
StatusID string `pg:",notnull"`
|
StatusID string `pg:"type:CHAR(26),notnull"`
|
||||||
// ActivityPub URI of this fave
|
// ActivityPub URI of this fave
|
||||||
URI string `pg:",notnull"`
|
URI string `pg:",notnull"`
|
||||||
|
|
||||||
|
@ -23,13 +23,13 @@ import "time"
|
|||||||
// StatusMute refers to one account having muted the status of another account or its own
|
// StatusMute refers to one account having muted the status of another account or its own
|
||||||
type StatusMute struct {
|
type StatusMute struct {
|
||||||
// id of this mute in the database
|
// id of this mute in the database
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// when was this mute created
|
// when was this mute created
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// id of the account that created ('did') the mute
|
// id of the account that created ('did') the mute
|
||||||
AccountID string `pg:",notnull"`
|
AccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// id the account owning the muted status (can be the same as accountID)
|
// id the account owning the muted status (can be the same as accountID)
|
||||||
TargetAccountID string `pg:",notnull"`
|
TargetAccountID string `pg:"type:CHAR(26),notnull"`
|
||||||
// database id of the status that has been muted
|
// database id of the status that has been muted
|
||||||
StatusID string `pg:",notnull"`
|
StatusID string `pg:"type:CHAR(26),notnull"`
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,13 @@ import "time"
|
|||||||
// Tag represents a hashtag for gathering public statuses together
|
// Tag represents a hashtag for gathering public statuses together
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
// id of this tag in the database
|
// id of this tag in the database
|
||||||
ID string `pg:",unique,type:uuid,default:gen_random_uuid(),pk,notnull"`
|
ID string `pg:",unique,type:CHAR(26),pk,notnull"`
|
||||||
// Href of this tag, eg https://example.org/tags/somehashtag
|
// Href of this tag, eg https://example.org/tags/somehashtag
|
||||||
URL string
|
URL string
|
||||||
// name of this tag -- the tag without the hash part
|
// name of this tag -- the tag without the hash part
|
||||||
Name string `pg:",unique,pk,notnull"`
|
Name string `pg:",unique,pk,notnull"`
|
||||||
// Which account ID is the first one we saw using this tag?
|
// Which account ID is the first one we saw using this tag?
|
||||||
FirstSeenFromAccountID string
|
FirstSeenFromAccountID string `pg:"type:CHAR(26)"`
|
||||||
// when was this tag created
|
// when was this tag created
|
||||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// when was this tag last updated
|
// when was this tag last updated
|
||||||
|
@ -31,11 +31,11 @@ type User struct {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// id of this user in the local database; the end-user will never need to know this, it's strictly internal
|
// id of this user in the local database; the end-user will never need to know this, it's strictly internal
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
ID string `pg:"type:CHAR(26),pk,notnull,unique"`
|
||||||
// confirmed email address for this user, this should be unique -- only one email address registered per instance, multiple users per email are not supported
|
// confirmed email address for this user, this should be unique -- only one email address registered per instance, multiple users per email are not supported
|
||||||
Email string `pg:"default:null,unique"`
|
Email string `pg:"default:null,unique"`
|
||||||
// The id of the local gtsmodel.Account entry for this user, if it exists (unconfirmed users don't have an account yet)
|
// The id of the local gtsmodel.Account entry for this user, if it exists (unconfirmed users don't have an account yet)
|
||||||
AccountID string `pg:"default:'',notnull,unique"`
|
AccountID string `pg:"type:CHAR(26),unique"`
|
||||||
// The encrypted password of this user, generated using https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword. A salt is included so we're safe against 🌈 tables
|
// The encrypted password of this user, generated using https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword. A salt is included so we're safe against 🌈 tables
|
||||||
EncryptedPassword string `pg:",notnull"`
|
EncryptedPassword string `pg:",notnull"`
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ type User struct {
|
|||||||
// How many times has this user signed in?
|
// How many times has this user signed in?
|
||||||
SignInCount int
|
SignInCount int
|
||||||
// id of the user who invited this user (who let this guy in?)
|
// id of the user who invited this user (who let this guy in?)
|
||||||
InviteID string
|
InviteID string `pg:"type:CHAR(26)"`
|
||||||
// What languages does this user want to see?
|
// What languages does this user want to see?
|
||||||
ChosenLanguages []string
|
ChosenLanguages []string
|
||||||
// What languages does this user not want to see?
|
// What languages does this user not want to see?
|
||||||
@ -68,7 +68,7 @@ type User struct {
|
|||||||
// In what timezone/locale is this user located?
|
// In what timezone/locale is this user located?
|
||||||
Locale string
|
Locale string
|
||||||
// Which application id created this user? See gtsmodel.Application
|
// Which application id created this user? See gtsmodel.Application
|
||||||
CreatedByApplicationID string
|
CreatedByApplicationID string `pg:"type:CHAR(26)"`
|
||||||
// When did we last contact this user
|
// When did we last contact this user
|
||||||
LastEmailedAt time.Time `pg:"type:timestamp"`
|
LastEmailedAt time.Time `pg:"type:timestamp"`
|
||||||
|
|
||||||
|
51
internal/id/ulid.go
Normal file
51
internal/id/ulid.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package id
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/oklog/ulid"
|
||||||
|
)
|
||||||
|
|
||||||
|
const randomRange = 631152381 // ~20 years in seconds
|
||||||
|
|
||||||
|
// NewULID returns a new ULID string using the current time, or an error if something goes wrong.
|
||||||
|
func NewULID() (string, error) {
|
||||||
|
newUlid, err := ulid.New(ulid.Timestamp(time.Now()), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return newUlid.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewULIDFromTime returns a new ULID string using the given time, or an error if something goes wrong.
|
||||||
|
func NewULIDFromTime(t time.Time) (string, error) {
|
||||||
|
newUlid, err := ulid.New(ulid.Timestamp(t), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return newUlid.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRandomULID returns a new ULID string using a random time in an ~80 year range around the current datetime, or an error if something goes wrong.
|
||||||
|
func NewRandomULID() (string, error) {
|
||||||
|
b1, err := rand.Int(rand.Reader, big.NewInt(randomRange))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
r1 := time.Duration(int(b1.Int64()))
|
||||||
|
|
||||||
|
b2, err := rand.Int(rand.Reader, big.NewInt(randomRange))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
r2 := -time.Duration(int(b2.Int64()))
|
||||||
|
|
||||||
|
arbitraryTime := time.Now().Add(r1 * time.Second).Add(r2 * time.Second)
|
||||||
|
newUlid, err := ulid.New(ulid.Timestamp(arbitraryTime), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return newUlid.String(), nil
|
||||||
|
}
|
@ -26,12 +26,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/blob"
|
"github.com/superseriousbusiness/gotosocial/internal/blob"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -242,9 +242,11 @@ func (mh *mediaHandler) ProcessLocalEmoji(emojiBytes []byte, shortcode string) (
|
|||||||
// create the urls and storage paths
|
// create the urls and storage paths
|
||||||
URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
||||||
|
|
||||||
// generate a uuid for the new emoji -- normally we could let the database do this for us,
|
// generate a id for the new emoji
|
||||||
// but we need it below so we should create it here instead.
|
newEmojiID, err := id.NewRandomULID()
|
||||||
newEmojiID := uuid.NewString()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// webfinger uri for the emoji -- unrelated to actually serving the image
|
// webfinger uri for the emoji -- unrelated to actually serving the image
|
||||||
// will be something like https://example.org/emoji/70a7f3d7-7e35-4098-8ce3-9b5e8203bb9c
|
// will be something like https://example.org/emoji/70a7f3d7-7e35-4098-8ce3-9b5e8203bb9c
|
||||||
|
@ -24,8 +24,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string, mediaType Type, accountID string, remoteURL string) (*gtsmodel.MediaAttachment, error) {
|
func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string, mediaType Type, accountID string, remoteURL string) (*gtsmodel.MediaAttachment, error) {
|
||||||
@ -72,9 +72,12 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
|
|||||||
return nil, fmt.Errorf("error deriving thumbnail: %s", err)
|
return nil, fmt.Errorf("error deriving thumbnail: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// now put it in storage, take a new uuid for the name of the file so we don't store any unnecessary info about it
|
// now put it in storage, take a new id for the name of the file so we don't store any unnecessary info about it
|
||||||
extension := strings.Split(contentType, "/")[1]
|
extension := strings.Split(contentType, "/")[1]
|
||||||
newMediaID := uuid.NewString()
|
newMediaID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
||||||
originalURL := fmt.Sprintf("%s/%s/%s/original/%s.%s", URLbase, accountID, mediaType, newMediaID, extension)
|
originalURL := fmt.Sprintf("%s/%s/%s/original/%s.%s", URLbase, accountID, mediaType, newMediaID, extension)
|
||||||
|
@ -24,8 +24,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (mh *mediaHandler) processImageAttachment(data []byte, accountID string, contentType string, remoteURL string) (*gtsmodel.MediaAttachment, error) {
|
func (mh *mediaHandler) processImageAttachment(data []byte, accountID string, contentType string, remoteURL string) (*gtsmodel.MediaAttachment, error) {
|
||||||
@ -58,9 +58,12 @@ func (mh *mediaHandler) processImageAttachment(data []byte, accountID string, co
|
|||||||
return nil, fmt.Errorf("error deriving thumbnail: %s", err)
|
return nil, fmt.Errorf("error deriving thumbnail: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// now put it in storage, take a new uuid for the name of the file so we don't store any unnecessary info about it
|
// now put it in storage, take a new id for the name of the file so we don't store any unnecessary info about it
|
||||||
extension := strings.Split(contentType, "/")[1]
|
extension := strings.Split(contentType, "/")[1]
|
||||||
newMediaID := uuid.NewString()
|
newMediaID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
||||||
originalURL := fmt.Sprintf("%s/%s/attachment/original/%s.%s", URLbase, accountID, newMediaID, extension)
|
originalURL := fmt.Sprintf("%s/%s/attachment/original/%s.%s", URLbase, accountID, newMediaID, extension)
|
||||||
|
@ -67,7 +67,7 @@ func (cs *clientStore) Delete(ctx context.Context, id string) error {
|
|||||||
|
|
||||||
// Client is a handy little wrapper for typical oauth client details
|
// Client is a handy little wrapper for typical oauth client details
|
||||||
type Client struct {
|
type Client struct {
|
||||||
ID string
|
ID string `pg:"type:CHAR(26),pk,notnull"`
|
||||||
Secret string
|
Secret string
|
||||||
Domain string
|
Domain string
|
||||||
UserID string
|
UserID string
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/oauth2/v4"
|
"github.com/superseriousbusiness/oauth2/v4"
|
||||||
"github.com/superseriousbusiness/oauth2/v4/models"
|
"github.com/superseriousbusiness/oauth2/v4/models"
|
||||||
)
|
)
|
||||||
@ -98,7 +99,17 @@ func (pts *tokenStore) Create(ctx context.Context, info oauth2.TokenInfo) error
|
|||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("info param was not a models.Token")
|
return errors.New("info param was not a models.Token")
|
||||||
}
|
}
|
||||||
if err := pts.db.Put(TokenToPGToken(t)); err != nil {
|
|
||||||
|
pgt := TokenToPGToken(t)
|
||||||
|
if pgt.ID == "" {
|
||||||
|
pgtID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pgt.ID = pgtID
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pts.db.Put(pgt); err != nil {
|
||||||
return fmt.Errorf("error in tokenstore create: %s", err)
|
return fmt.Errorf("error in tokenstore create: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -176,7 +187,7 @@ func (pts *tokenStore) GetByRefresh(ctx context.Context, refresh string) (oauth2
|
|||||||
// As such, manual translation is always required between Token and the gotosocial *model.Token. The helper functions oauthTokenToPGToken
|
// As such, manual translation is always required between Token and the gotosocial *model.Token. The helper functions oauthTokenToPGToken
|
||||||
// and pgTokenToOauthToken can be used for that.
|
// and pgTokenToOauthToken can be used for that.
|
||||||
type Token struct {
|
type Token struct {
|
||||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"`
|
ID string `pg:"type:CHAR(26),pk,notnull"`
|
||||||
ClientID string
|
ClientID string
|
||||||
UserID string
|
UserID string
|
||||||
RedirectURI string
|
RedirectURI string
|
||||||
|
@ -22,11 +22,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
@ -426,8 +426,10 @@ func (p *processor) AccountFollowCreate(authed *oauth.Auth, form *apimodel.Accou
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make the follow request
|
// make the follow request
|
||||||
|
newFollowID, err := id.NewRandomULID()
|
||||||
newFollowID := uuid.NewString()
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
fr := >smodel.FollowRequest{
|
fr := >smodel.FollowRequest{
|
||||||
ID: newFollowID,
|
ID: newFollowID,
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -53,6 +54,12 @@ func (p *processor) AdminEmojiCreate(authed *oauth.Auth, form *apimodel.EmojiCre
|
|||||||
return nil, fmt.Errorf("error reading emoji: %s", err)
|
return nil, fmt.Errorf("error reading emoji: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emojiID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
emoji.ID = emojiID
|
||||||
|
|
||||||
mastoEmoji, err := p.tc.EmojiToMasto(emoji)
|
mastoEmoji, err := p.tc.EmojiToMasto(emoji)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error converting emoji to mastotype: %s", err)
|
return nil, fmt.Errorf("error converting emoji to mastotype: %s", err)
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,12 +36,21 @@ func (p *processor) AppCreate(authed *oauth.Auth, form *apimodel.ApplicationCrea
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate new IDs for this application and its associated client
|
// generate new IDs for this application and its associated client
|
||||||
clientID := uuid.NewString()
|
clientID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
clientSecret := uuid.NewString()
|
clientSecret := uuid.NewString()
|
||||||
vapidKey := uuid.NewString()
|
vapidKey := uuid.NewString()
|
||||||
|
|
||||||
|
appID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// generate the application to put in the database
|
// generate the application to put in the database
|
||||||
app := >smodel.Application{
|
app := >smodel.Application{
|
||||||
|
ID: appID,
|
||||||
Name: form.ClientName,
|
Name: form.ClientName,
|
||||||
Website: form.Website,
|
Website: form.Website,
|
||||||
RedirectURI: form.RedirectURIs,
|
RedirectURI: form.RedirectURIs,
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,7 +75,12 @@ func (p *processor) authenticateAndDereferenceFediRequest(username string, r *ht
|
|||||||
return nil, fmt.Errorf("couldn't convert dereferenced uri %s to gtsmodel account: %s", requestingAccountURI.String(), err)
|
return nil, fmt.Errorf("couldn't convert dereferenced uri %s to gtsmodel account: %s", requestingAccountURI.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shove it in the database for later
|
requestingAccountID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
requestingAccount.ID = requestingAccountID
|
||||||
|
|
||||||
if err := p.db.Put(requestingAccount); err != nil {
|
if err := p.db.Put(requestingAccount); err != nil {
|
||||||
return nil, fmt.Errorf("database error inserting account with uri %s: %s", requestingAccountURI.String(), err)
|
return nil, fmt.Errorf("database error inserting account with uri %s: %s", requestingAccountURI.String(), err)
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) notifyStatus(status *gtsmodel.Status) error {
|
func (p *processor) notifyStatus(status *gtsmodel.Status) error {
|
||||||
@ -79,7 +80,13 @@ func (p *processor) notifyStatus(status *gtsmodel.Status) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if we've reached this point we know the mention is for a local account, and the notification doesn't exist, so create it
|
// if we've reached this point we know the mention is for a local account, and the notification doesn't exist, so create it
|
||||||
|
notifID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
notif := >smodel.Notification{
|
notif := >smodel.Notification{
|
||||||
|
ID: notifID,
|
||||||
NotificationType: gtsmodel.NotificationMention,
|
NotificationType: gtsmodel.NotificationMention,
|
||||||
TargetAccountID: m.TargetAccountID,
|
TargetAccountID: m.TargetAccountID,
|
||||||
OriginAccountID: status.AccountID,
|
OriginAccountID: status.AccountID,
|
||||||
@ -100,7 +107,13 @@ func (p *processor) notifyFollowRequest(followRequest *gtsmodel.FollowRequest, r
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
notif := >smodel.Notification{
|
notif := >smodel.Notification{
|
||||||
|
ID: notifID,
|
||||||
NotificationType: gtsmodel.NotificationFollowRequest,
|
NotificationType: gtsmodel.NotificationFollowRequest,
|
||||||
TargetAccountID: followRequest.TargetAccountID,
|
TargetAccountID: followRequest.TargetAccountID,
|
||||||
OriginAccountID: followRequest.AccountID,
|
OriginAccountID: followRequest.AccountID,
|
||||||
@ -129,7 +142,13 @@ func (p *processor) notifyFollow(follow *gtsmodel.Follow, receivingAccount *gtsm
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now create the new follow notification
|
// now create the new follow notification
|
||||||
|
notifID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
notif := >smodel.Notification{
|
notif := >smodel.Notification{
|
||||||
|
ID: notifID,
|
||||||
NotificationType: gtsmodel.NotificationFollow,
|
NotificationType: gtsmodel.NotificationFollow,
|
||||||
TargetAccountID: follow.TargetAccountID,
|
TargetAccountID: follow.TargetAccountID,
|
||||||
OriginAccountID: follow.AccountID,
|
OriginAccountID: follow.AccountID,
|
||||||
@ -147,7 +166,13 @@ func (p *processor) notifyFave(fave *gtsmodel.StatusFave, receivingAccount *gtsm
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
notif := >smodel.Notification{
|
notif := >smodel.Notification{
|
||||||
|
ID: notifID,
|
||||||
NotificationType: gtsmodel.NotificationFave,
|
NotificationType: gtsmodel.NotificationFave,
|
||||||
TargetAccountID: fave.TargetAccountID,
|
TargetAccountID: fave.TargetAccountID,
|
||||||
OriginAccountID: fave.AccountID,
|
OriginAccountID: fave.AccountID,
|
||||||
@ -200,7 +225,13 @@ func (p *processor) notifyAnnounce(status *gtsmodel.Status) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now create the new reblog notification
|
// now create the new reblog notification
|
||||||
|
notifID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
notif := >smodel.Notification{
|
notif := >smodel.Notification{
|
||||||
|
ID: notifID,
|
||||||
NotificationType: gtsmodel.NotificationReblog,
|
NotificationType: gtsmodel.NotificationReblog,
|
||||||
TargetAccountID: boostedAcct.ID,
|
TargetAccountID: boostedAcct.ID,
|
||||||
OriginAccountID: status.AccountID,
|
OriginAccountID: status.AccountID,
|
||||||
|
@ -23,10 +23,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) processFromFederator(federatorMsg gtsmodel.FromFederator) error {
|
func (p *processor) processFromFederator(federatorMsg gtsmodel.FromFederator) error {
|
||||||
@ -109,6 +109,12 @@ func (p *processor) processFromFederator(federatorMsg gtsmodel.FromFederator) er
|
|||||||
return fmt.Errorf("error dereferencing announce from federator: %s", err)
|
return fmt.Errorf("error dereferencing announce from federator: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
incomingAnnounceID, err := id.NewULIDFromTime(incomingAnnounce.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
incomingAnnounce.ID = incomingAnnounceID
|
||||||
|
|
||||||
if err := p.db.Put(incomingAnnounce); err != nil {
|
if err := p.db.Put(incomingAnnounce); err != nil {
|
||||||
if _, ok := err.(db.ErrAlreadyExists); !ok {
|
if _, ok := err.(db.ErrAlreadyExists); !ok {
|
||||||
return fmt.Errorf("error adding dereferenced announce to the db: %s", err)
|
return fmt.Errorf("error adding dereferenced announce to the db: %s", err)
|
||||||
@ -212,7 +218,11 @@ func (p *processor) dereferenceStatusFields(status *gtsmodel.Status, requestingU
|
|||||||
// the status should have an ID by now, but just in case it doesn't let's generate one here
|
// the status should have an ID by now, but just in case it doesn't let's generate one here
|
||||||
// because we'll need it further down
|
// because we'll need it further down
|
||||||
if status.ID == "" {
|
if status.ID == "" {
|
||||||
status.ID = uuid.NewString()
|
newID, err := id.NewULIDFromTime(status.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
status.ID = newID
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Media attachments.
|
// 1. Media attachments.
|
||||||
@ -364,12 +374,12 @@ func (p *processor) dereferenceAnnounce(announce *gtsmodel.Status, requestingUse
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we don't have it so we need to dereference it
|
// we don't have it so we need to dereference it
|
||||||
remoteStatusID, err := url.Parse(announce.GTSBoostedStatus.URI)
|
remoteStatusURI, err := url.Parse(announce.GTSBoostedStatus.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dereferenceAnnounce: error parsing url %s: %s", announce.GTSBoostedStatus.URI, err)
|
return fmt.Errorf("dereferenceAnnounce: error parsing url %s: %s", announce.GTSBoostedStatus.URI, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
statusable, err := p.federator.DereferenceRemoteStatus(requestingUsername, remoteStatusID)
|
statusable, err := p.federator.DereferenceRemoteStatus(requestingUsername, remoteStatusURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dereferenceAnnounce: error dereferencing remote status with id %s: %s", announce.GTSBoostedStatus.URI, err)
|
return fmt.Errorf("dereferenceAnnounce: error dereferencing remote status with id %s: %s", announce.GTSBoostedStatus.URI, err)
|
||||||
}
|
}
|
||||||
@ -397,7 +407,12 @@ func (p *processor) dereferenceAnnounce(announce *gtsmodel.Status, requestingUse
|
|||||||
return fmt.Errorf("dereferenceAnnounce: error converting dereferenced account with id %s into account : %s", accountURI.String(), err)
|
return fmt.Errorf("dereferenceAnnounce: error converting dereferenced account with id %s into account : %s", accountURI.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the dereferenced account so it gets an ID etc
|
accountID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
account.ID = accountID
|
||||||
|
|
||||||
if err := p.db.Put(account); err != nil {
|
if err := p.db.Put(account); err != nil {
|
||||||
return fmt.Errorf("dereferenceAnnounce: error putting dereferenced account with id %s into database : %s", accountURI.String(), err)
|
return fmt.Errorf("dereferenceAnnounce: error putting dereferenced account with id %s into database : %s", accountURI.String(), err)
|
||||||
}
|
}
|
||||||
@ -413,7 +428,12 @@ func (p *processor) dereferenceAnnounce(announce *gtsmodel.Status, requestingUse
|
|||||||
return fmt.Errorf("dereferenceAnnounce: error converting dereferenced statusable with id %s into status : %s", announce.GTSBoostedStatus.URI, err)
|
return fmt.Errorf("dereferenceAnnounce: error converting dereferenced statusable with id %s into status : %s", announce.GTSBoostedStatus.URI, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// put it in the db already so it gets an ID generated for it
|
boostedStatusID, err := id.NewULIDFromTime(boostedStatus.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
boostedStatus.ID = boostedStatusID
|
||||||
|
|
||||||
if err := p.db.Put(boostedStatus); err != nil {
|
if err := p.db.Put(boostedStatus); err != nil {
|
||||||
return fmt.Errorf("dereferenceAnnounce: error putting dereferenced status with id %s into the db: %s", announce.GTSBoostedStatus.URI, err)
|
return fmt.Errorf("dereferenceAnnounce: error putting dereferenced status with id %s into the db: %s", announce.GTSBoostedStatus.URI, err)
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
@ -168,7 +169,12 @@ func (p *processor) searchStatusByURI(authed *oauth.Auth, uri *url.URL, resolve
|
|||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// put it in the DB so it gets a UUID
|
statusID, err := id.NewULIDFromTime(status.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status.ID = statusID
|
||||||
|
|
||||||
if err := p.db.Put(status); err != nil {
|
if err := p.db.Put(status); err != nil {
|
||||||
return nil, fmt.Errorf("error putting status in the db: %s", err)
|
return nil, fmt.Errorf("error putting status in the db: %s", err)
|
||||||
}
|
}
|
||||||
@ -211,6 +217,12 @@ func (p *processor) searchAccountByURI(authed *oauth.Auth, uri *url.URL, resolve
|
|||||||
return nil, fmt.Errorf("searchAccountByURI: error dereferencing account with uri %s: %s", uri.String(), err)
|
return nil, fmt.Errorf("searchAccountByURI: error dereferencing account with uri %s: %s", uri.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accountID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
account.ID = accountID
|
||||||
|
|
||||||
if err := p.db.Put(account); err != nil {
|
if err := p.db.Put(account); err != nil {
|
||||||
return nil, fmt.Errorf("searchAccountByURI: error inserting account with uri %s: %s", uri.String(), err)
|
return nil, fmt.Errorf("searchAccountByURI: error inserting account with uri %s: %s", uri.String(), err)
|
||||||
}
|
}
|
||||||
@ -281,6 +293,12 @@ func (p *processor) searchAccountByMention(authed *oauth.Auth, mention string, r
|
|||||||
return nil, fmt.Errorf("searchAccountByMention: error converting account with uri %s: %s", acctURI.String(), err)
|
return nil, fmt.Errorf("searchAccountByMention: error converting account with uri %s: %s", acctURI.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foundAccountID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
foundAccount.ID = foundAccountID
|
||||||
|
|
||||||
// put this new account in our database
|
// put this new account in our database
|
||||||
if err := p.db.Put(foundAccount); err != nil {
|
if err := p.db.Put(foundAccount); err != nil {
|
||||||
return nil, fmt.Errorf("searchAccountByMention: error inserting account with uri %s: %s", acctURI.String(), err)
|
return nil, fmt.Errorf("searchAccountByMention: error inserting account with uri %s: %s", acctURI.String(), err)
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func (p *processor) Context(account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) {
|
func (p *processor) Context(account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) {
|
||||||
return &apimodel.Context{
|
return &apimodel.Context{
|
||||||
Ancestors: []apimodel.Status{},
|
Ancestors: []apimodel.Status{},
|
||||||
Descendants: []apimodel.Status{},
|
Descendants: []apimodel.Status{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,19 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) Create(account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
|
func (p *processor) Create(account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
|
||||||
uris := util.GenerateURIsForAccount(account.Username, p.config.Protocol, p.config.Host)
|
uris := util.GenerateURIsForAccount(account.Username, p.config.Protocol, p.config.Host)
|
||||||
thisStatusID := uuid.NewString()
|
thisStatusID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
thisStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, thisStatusID)
|
thisStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, thisStatusID)
|
||||||
thisStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, thisStatusID)
|
thisStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, thisStatusID)
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -66,7 +66,10 @@ func (p *processor) Fave(account *gtsmodel.Account, targetStatusID string) (*api
|
|||||||
}
|
}
|
||||||
|
|
||||||
if newFave {
|
if newFave {
|
||||||
thisFaveID := uuid.NewString()
|
thisFaveID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
// we need to create a new fave in the database
|
// we need to create a new fave in the database
|
||||||
gtsFave := >smodel.StatusFave{
|
gtsFave := >smodel.StatusFave{
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -183,6 +184,12 @@ func (p *processor) processMentions(form *apimodel.AdvancedStatusCreateForm, acc
|
|||||||
return fmt.Errorf("error generating mentions from status: %s", err)
|
return fmt.Errorf("error generating mentions from status: %s", err)
|
||||||
}
|
}
|
||||||
for _, menchie := range gtsMenchies {
|
for _, menchie := range gtsMenchies {
|
||||||
|
menchieID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
menchie.ID = menchieID
|
||||||
|
|
||||||
if err := p.db.Put(menchie); err != nil {
|
if err := p.db.Put(menchie); err != nil {
|
||||||
return fmt.Errorf("error putting mentions in db: %s", err)
|
return fmt.Errorf("error putting mentions in db: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -35,12 +35,12 @@ import (
|
|||||||
|
|
||||||
func (p *processor) HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.StatusTimelineResponse, gtserror.WithCode) {
|
func (p *processor) HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.StatusTimelineResponse, gtserror.WithCode) {
|
||||||
l := p.log.WithFields(logrus.Fields{
|
l := p.log.WithFields(logrus.Fields{
|
||||||
"func": "HomeTimelineGet",
|
"func": "HomeTimelineGet",
|
||||||
"maxID": maxID,
|
"maxID": maxID,
|
||||||
"sinceID": sinceID,
|
"sinceID": sinceID,
|
||||||
"minID": minID,
|
"minID": minID,
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
"local": local,
|
"local": local,
|
||||||
})
|
})
|
||||||
|
|
||||||
resp := &apimodel.StatusTimelineResponse{
|
resp := &apimodel.StatusTimelineResponse{
|
||||||
@ -53,7 +53,7 @@ func (p *processor) HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID st
|
|||||||
sinceIDMarker := sinceID
|
sinceIDMarker := sinceID
|
||||||
minIDMarker := minID
|
minIDMarker := minID
|
||||||
|
|
||||||
l.Debugf("\n entering grabloop \n")
|
l.Debugf("\n entering grabloop \n")
|
||||||
grabloop:
|
grabloop:
|
||||||
for len(apiStatuses) < limit {
|
for len(apiStatuses) < limit {
|
||||||
l.Debugf("\n querying the db \n")
|
l.Debugf("\n querying the db \n")
|
||||||
|
@ -4,8 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,7 +25,10 @@ func (c *converter) FollowRequestToFollow(f *gtsmodel.FollowRequest) *gtsmodel.F
|
|||||||
func (c *converter) StatusToBoost(s *gtsmodel.Status, boostingAccount *gtsmodel.Account) (*gtsmodel.Status, error) {
|
func (c *converter) StatusToBoost(s *gtsmodel.Status, boostingAccount *gtsmodel.Account) (*gtsmodel.Status, error) {
|
||||||
// the wrapper won't use the same ID as the boosted status so we generate some new UUIDs
|
// the wrapper won't use the same ID as the boosted status so we generate some new UUIDs
|
||||||
uris := util.GenerateURIsForAccount(boostingAccount.Username, c.config.Protocol, c.config.Host)
|
uris := util.GenerateURIsForAccount(boostingAccount.Username, c.config.Protocol, c.config.Host)
|
||||||
boostWrapperStatusID := uuid.NewString()
|
boostWrapperStatusID, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
boostWrapperStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, boostWrapperStatusID)
|
boostWrapperStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, boostWrapperStatusID)
|
||||||
boostWrapperStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, boostWrapperStatusID)
|
boostWrapperStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, boostWrapperStatusID)
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/go-fed/activity/streams"
|
"github.com/go-fed/activity/streams"
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
"github.com/go-fed/activity/streams/vocab"
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,7 +25,13 @@ func (c *converter) WrapPersonInUpdate(person vocab.ActivityStreamsPerson, origi
|
|||||||
update.SetActivityStreamsActor(actorProp)
|
update.SetActivityStreamsActor(actorProp)
|
||||||
|
|
||||||
// set the ID
|
// set the ID
|
||||||
idString := util.GenerateURIForUpdate(originAccount.Username, c.config.Protocol, c.config.Host, uuid.NewString())
|
|
||||||
|
newID, err := id.NewRandomULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
idString := util.GenerateURIForUpdate(originAccount.Username, c.config.Protocol, c.config.Host, newID)
|
||||||
idURI, err := url.Parse(idString)
|
idURI, err := url.Parse(idString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("WrapPersonInUpdate: error parsing url %s: %s", idString, err)
|
return nil, fmt.Errorf("WrapPersonInUpdate: error parsing url %s: %s", idString, err)
|
||||||
|
@ -85,21 +85,20 @@ var (
|
|||||||
// followingPathRegex parses a path that validates and captures the username part from eg /users/example_username/following
|
// followingPathRegex parses a path that validates and captures the username part from eg /users/example_username/following
|
||||||
followingPathRegex = regexp.MustCompile(followingPathRegexString)
|
followingPathRegex = regexp.MustCompile(followingPathRegexString)
|
||||||
|
|
||||||
// see https://ihateregex.io/expr/uuid/
|
ulidRegexString = `[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}`
|
||||||
uuidRegexString = `[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}`
|
|
||||||
|
|
||||||
likedPathRegexString = fmt.Sprintf(`^/?%s/(%s)/%s$`, UsersPath, usernameRegexString, LikedPath)
|
likedPathRegexString = fmt.Sprintf(`^/?%s/(%s)/%s$`, UsersPath, usernameRegexString, LikedPath)
|
||||||
// likedPathRegex parses a path that validates and captures the username part from eg /users/example_username/liked
|
// likedPathRegex parses a path that validates and captures the username part from eg /users/example_username/liked
|
||||||
likedPathRegex = regexp.MustCompile(likedPathRegexString)
|
likedPathRegex = regexp.MustCompile(likedPathRegexString)
|
||||||
|
|
||||||
likePathRegexString = fmt.Sprintf(`^/?%s/(%s)/%s/(%s)$`, UsersPath, usernameRegexString, LikedPath, uuidRegexString)
|
likePathRegexString = fmt.Sprintf(`^/?%s/(%s)/%s/(%s)$`, UsersPath, usernameRegexString, LikedPath, ulidRegexString)
|
||||||
// likePathRegex parses a path that validates and captures the username part and the uuid part
|
// likePathRegex parses a path that validates and captures the username part and the ulid part
|
||||||
// from eg /users/example_username/liked/123e4567-e89b-12d3-a456-426655440000.
|
// from eg /users/example_username/like/01F7XT5JZW1WMVSW1KADS8PVDH
|
||||||
likePathRegex = regexp.MustCompile(likePathRegexString)
|
likePathRegex = regexp.MustCompile(likePathRegexString)
|
||||||
|
|
||||||
statusesPathRegexString = fmt.Sprintf(`^/?%s/(%s)/%s/(%s)$`, UsersPath, usernameRegexString, StatusesPath, uuidRegexString)
|
statusesPathRegexString = fmt.Sprintf(`^/?%s/(%s)/%s/(%s)$`, UsersPath, usernameRegexString, StatusesPath, ulidRegexString)
|
||||||
// statusesPathRegex parses a path that validates and captures the username part and the uuid part
|
// statusesPathRegex parses a path that validates and captures the username part and the ulid part
|
||||||
// from eg /users/example_username/statuses/123e4567-e89b-12d3-a456-426655440000.
|
// from eg /users/example_username/statuses/01F7XT5JZW1WMVSW1KADS8PVDH
|
||||||
// The regex can be played with here: https://regex101.com/r/G9zuxQ/1
|
// The regex can be played with here: https://regex101.com/r/G9zuxQ/1
|
||||||
statusesPathRegex = regexp.MustCompile(statusesPathRegexString)
|
statusesPathRegex = regexp.MustCompile(statusesPathRegexString)
|
||||||
)
|
)
|
||||||
|
@ -108,19 +108,19 @@ type UserURIs struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GenerateURIForFollow returns the AP URI for a new follow -- something like:
|
// GenerateURIForFollow returns the AP URI for a new follow -- something like:
|
||||||
// https://example.org/users/whatever_user/follow/41c7f33f-1060-48d9-84df-38dcb13cf0d8
|
// https://example.org/users/whatever_user/follow/01F7XTH1QGBAPMGF49WJZ91XGC
|
||||||
func GenerateURIForFollow(username string, protocol string, host string, thisFollowID string) string {
|
func GenerateURIForFollow(username string, protocol string, host string, thisFollowID string) string {
|
||||||
return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, FollowPath, thisFollowID)
|
return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, FollowPath, thisFollowID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateURIForLike returns the AP URI for a new like/fave -- something like:
|
// GenerateURIForLike returns the AP URI for a new like/fave -- something like:
|
||||||
// https://example.org/users/whatever_user/liked/41c7f33f-1060-48d9-84df-38dcb13cf0d8
|
// https://example.org/users/whatever_user/liked/01F7XTH1QGBAPMGF49WJZ91XGC
|
||||||
func GenerateURIForLike(username string, protocol string, host string, thisFavedID string) string {
|
func GenerateURIForLike(username string, protocol string, host string, thisFavedID string) string {
|
||||||
return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, LikedPath, thisFavedID)
|
return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, LikedPath, thisFavedID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateURIForUpdate returns the AP URI for a new update activity -- something like:
|
// GenerateURIForUpdate returns the AP URI for a new update activity -- something like:
|
||||||
// https://example.org/users/whatever_user#updates/41c7f33f-1060-48d9-84df-38dcb13cf0d8
|
// https://example.org/users/whatever_user#updates/01F7XTH1QGBAPMGF49WJZ91XGC
|
||||||
func GenerateURIForUpdate(username string, protocol string, host string, thisUpdateID string) string {
|
func GenerateURIForUpdate(username string, protocol string, host string, thisUpdateID string) string {
|
||||||
return fmt.Sprintf("%s://%s/%s/%s#%s/%s", protocol, host, UsersPath, username, UpdatePath, thisUpdateID)
|
return fmt.Sprintf("%s://%s/%s/%s#%s/%s", protocol, host, UsersPath, username, UpdatePath, thisUpdateID)
|
||||||
}
|
}
|
||||||
@ -195,25 +195,25 @@ func IsLikedPath(id *url.URL) bool {
|
|||||||
return likedPathRegex.MatchString(strings.ToLower(id.Path))
|
return likedPathRegex.MatchString(strings.ToLower(id.Path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLikePath returns true if the given URL path corresponds to eg /users/example_username/liked/SOME_UUID_OF_A_STATUS
|
// IsLikePath returns true if the given URL path corresponds to eg /users/example_username/liked/SOME_ULID_OF_A_STATUS
|
||||||
func IsLikePath(id *url.URL) bool {
|
func IsLikePath(id *url.URL) bool {
|
||||||
return likePathRegex.MatchString(strings.ToLower(id.Path))
|
return likePathRegex.MatchString(strings.ToLower(id.Path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsStatusesPath returns true if the given URL path corresponds to eg /users/example_username/statuses/SOME_UUID_OF_A_STATUS
|
// IsStatusesPath returns true if the given URL path corresponds to eg /users/example_username/statuses/SOME_ULID_OF_A_STATUS
|
||||||
func IsStatusesPath(id *url.URL) bool {
|
func IsStatusesPath(id *url.URL) bool {
|
||||||
return statusesPathRegex.MatchString(strings.ToLower(id.Path))
|
return statusesPathRegex.MatchString(strings.ToLower(id.Path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseStatusesPath returns the username and uuid from a path such as /users/example_username/statuses/SOME_UUID_OF_A_STATUS
|
// ParseStatusesPath returns the username and ulid from a path such as /users/example_username/statuses/SOME_ULID_OF_A_STATUS
|
||||||
func ParseStatusesPath(id *url.URL) (username string, uuid string, err error) {
|
func ParseStatusesPath(id *url.URL) (username string, ulid string, err error) {
|
||||||
matches := statusesPathRegex.FindStringSubmatch(id.Path)
|
matches := statusesPathRegex.FindStringSubmatch(id.Path)
|
||||||
if len(matches) != 3 {
|
if len(matches) != 3 {
|
||||||
err = fmt.Errorf("expected 3 matches but matches length was %d", len(matches))
|
err = fmt.Errorf("expected 3 matches but matches length was %d", len(matches))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
username = matches[1]
|
username = matches[1]
|
||||||
uuid = matches[2]
|
ulid = matches[2]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,14 +272,14 @@ func ParseFollowingPath(id *url.URL) (username string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseLikedPath returns the username and uuid from a path such as /users/example_username/liked/SOME_UUID_OF_A_STATUS
|
// ParseLikedPath returns the username and ulid from a path such as /users/example_username/liked/SOME_ULID_OF_A_STATUS
|
||||||
func ParseLikedPath(id *url.URL) (username string, uuid string, err error) {
|
func ParseLikedPath(id *url.URL) (username string, ulid string, err error) {
|
||||||
matches := likePathRegex.FindStringSubmatch(id.Path)
|
matches := likePathRegex.FindStringSubmatch(id.Path)
|
||||||
if len(matches) != 3 {
|
if len(matches) != 3 {
|
||||||
err = fmt.Errorf("expected 3 matches but matches length was %d", len(matches))
|
err = fmt.Errorf("expected 3 matches but matches length was %d", len(matches))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
username = matches[1]
|
username = matches[1]
|
||||||
uuid = matches[2]
|
ulid = matches[2]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user