This commit is contained in:
parent
08f6de0bd7
commit
5e93ecee73
12 changed files with 241 additions and 109 deletions
|
@ -2,6 +2,7 @@ package activitypub
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -19,41 +20,29 @@ import (
|
||||||
webshared "git.mstar.dev/mstar/linstrom/web/shared"
|
webshared "git.mstar.dev/mstar/linstrom/web/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
var interestingMetadata = []string{
|
type inboundImportUserKey struct {
|
||||||
"nodeDescription",
|
|
||||||
"maintainer",
|
|
||||||
"tosUrl",
|
|
||||||
"nodeAdmins",
|
|
||||||
"privacyPolicyUrl",
|
|
||||||
"inquiryUrl",
|
|
||||||
"impressumUrl",
|
|
||||||
"donationUrl",
|
|
||||||
"staffAccounts",
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImportRemoteAccount(targetName string) (string, error) {
|
|
||||||
type InboundUserKey struct {
|
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Owner string `json:"owner"`
|
Owner string `json:"owner"`
|
||||||
Pem string `json:"publicKeyPem"`
|
Pem string `json:"publicKeyPem"`
|
||||||
}
|
}
|
||||||
type InboundUserMedia struct {
|
type inboundImportUserMedia struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
MediaType string `json:"mediaType"`
|
MediaType string `json:"mediaType"`
|
||||||
}
|
}
|
||||||
type InboundUser struct {
|
type inboundImportUser struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
PreferredUsername string `json:"preferredUsername"`
|
PreferredUsername string `json:"preferredUsername"`
|
||||||
Inbox string `json:"inbox"`
|
Inbox string `json:"inbox"`
|
||||||
PublicKey *InboundUserKey `json:"publicKey"`
|
Outbox *string `json:"outbox"`
|
||||||
|
PublicKey *inboundImportUserKey `json:"publicKey"`
|
||||||
Published *time.Time `json:"published"`
|
Published *time.Time `json:"published"`
|
||||||
DisplayName *string `json:"name"`
|
DisplayName *string `json:"name"`
|
||||||
Description *string `json:"summary,omitempty"`
|
Description *string `json:"summary,omitempty"`
|
||||||
PublicUrl *string `json:"url"`
|
PublicUrl *string `json:"url"`
|
||||||
Icon *InboundUserMedia `json:"icon,omitempty"`
|
Icon *inboundImportUserMedia `json:"icon,omitempty"`
|
||||||
Banner *InboundUserMedia `json:"image,omitempty"`
|
Banner *inboundImportUserMedia `json:"image,omitempty"`
|
||||||
Discoverable *bool `json:"discoverable"`
|
Discoverable *bool `json:"discoverable"`
|
||||||
Location *string `json:"vcard:Address,omitempty"`
|
Location *string `json:"vcard:Address,omitempty"`
|
||||||
Birthday *string `json:"vcard:bday,omitempty"`
|
Birthday *string `json:"vcard:bday,omitempty"`
|
||||||
|
@ -61,6 +50,9 @@ func ImportRemoteAccount(targetName string) (string, error) {
|
||||||
IsCat bool `json:"isCat"`
|
IsCat bool `json:"isCat"`
|
||||||
RestrictedFollow *bool `json:"manuallyApprovesFollowers"`
|
RestrictedFollow *bool `json:"manuallyApprovesFollowers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ImportRemoteAccount(targetName string) (string, error) {
|
||||||
|
|
||||||
// Get the target user's link first
|
// Get the target user's link first
|
||||||
webfinger, err := GetAccountWebfinger(targetName)
|
webfinger, err := GetAccountWebfinger(targetName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,14 +85,137 @@ func ImportRemoteAccount(targetName string) (string, error) {
|
||||||
if response.StatusCode != 200 {
|
if response.StatusCode != 200 {
|
||||||
return "", fmt.Errorf("activitypub: invalid status code: %v", response.StatusCode)
|
return "", fmt.Errorf("activitypub: invalid status code: %v", response.StatusCode)
|
||||||
}
|
}
|
||||||
var data InboundUser
|
var data inboundImportUser
|
||||||
err = json.Unmarshal(body, &data)
|
err = json.Unmarshal(body, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", other.Error("activitypub", "failed to unmarshal response", err)
|
return "", other.Error("activitypub", "failed to unmarshal response", err)
|
||||||
}
|
}
|
||||||
log.Debug().Any("received-data", data).Msg("Response data")
|
log.Debug().Any("received-data", data).Msg("Response data")
|
||||||
|
|
||||||
|
// TODO: Store received user in db
|
||||||
|
_, host, _ := SplitFullHandle(targetName)
|
||||||
|
hostId, err := ImportRemoteServer(host)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("activitypub", "failed to import host of target user", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := dbgen.User.
|
||||||
|
Where(dbgen.User.Username.Eq(targetName)).
|
||||||
|
Where(dbgen.User.ServerId.Eq(hostId)).
|
||||||
|
Preload(dbgen.User.RemoteInfo).
|
||||||
|
Preload(dbgen.User.InfoFields).
|
||||||
|
Preload(dbgen.User.BeingTypes).
|
||||||
|
FirstOrCreate()
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("activitypub", "failed to find or create user in db", err)
|
||||||
|
}
|
||||||
|
user.Verified = true
|
||||||
|
user.FinishedRegistration = true
|
||||||
|
|
||||||
|
if user.RemoteInfo == nil {
|
||||||
|
user.RemoteInfo = &models.UserRemoteLinks{
|
||||||
|
UserId: user.ID,
|
||||||
|
ApLink: data.Id,
|
||||||
|
}
|
||||||
|
err = dbgen.UserRemoteLinks.Create(user.RemoteInfo)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("activitypub", "failed to create remote data for user", err)
|
||||||
|
}
|
||||||
|
err = dbgen.User.RemoteInfo.Model(user).Replace(user.RemoteInfo)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("activitypub", "failed to connect remote data to user", err)
|
||||||
|
}
|
||||||
|
user.RemoteInfoId.Int64 = int64(user.RemoteInfo.ID)
|
||||||
|
user.RemoteInfoId.Valid = true
|
||||||
|
}
|
||||||
|
if data.DisplayName != nil {
|
||||||
|
user.DisplayName = *data.DisplayName
|
||||||
|
}
|
||||||
|
if data.Outbox != nil {
|
||||||
|
user.RemoteInfo.OutboxLink.String = *data.Outbox
|
||||||
|
user.RemoteInfo.OutboxLink.Valid = true
|
||||||
|
}
|
||||||
|
user.RemoteInfo.InboxLink = data.Inbox
|
||||||
|
if data.PublicKey != nil {
|
||||||
|
pemBlock, _ := pem.Decode([]byte(data.PublicKey.Pem))
|
||||||
|
if pemBlock.Type != "PUBLIC KEY" && pemBlock.Type != "RSA PUBLIC KEY" {
|
||||||
|
return "", fmt.Errorf("activitypub: invalid public key block type: %v", pemBlock.Type)
|
||||||
|
}
|
||||||
|
user.PublicKeyRsa = pemBlock.Bytes
|
||||||
|
}
|
||||||
|
// Assume published day of user won't change
|
||||||
|
if data.Description != nil {
|
||||||
|
user.Description = *data.Description
|
||||||
|
}
|
||||||
|
if data.PublicUrl != nil {
|
||||||
|
user.RemoteInfo.ViewLink.String = *data.PublicUrl
|
||||||
|
user.RemoteInfo.ViewLink.Valid = true
|
||||||
|
}
|
||||||
|
if data.Discoverable != nil {
|
||||||
|
user.Indexable = *data.Discoverable
|
||||||
|
} else {
|
||||||
|
// Assume false per default
|
||||||
|
user.Indexable = false
|
||||||
|
}
|
||||||
|
if data.Location != nil {
|
||||||
|
user.Location.String = *data.Location
|
||||||
|
user.Location.Valid = true
|
||||||
|
}
|
||||||
|
if data.Birthday != nil {
|
||||||
|
user.Birthday.String = *data.Birthday
|
||||||
|
user.Birthday.Valid = true
|
||||||
|
}
|
||||||
|
if data.RestrictedFollow != nil {
|
||||||
|
user.RestrictedFollow = *data.RestrictedFollow
|
||||||
|
} else {
|
||||||
|
// Assume not restricted if not included in received data
|
||||||
|
user.RestrictedFollow = false
|
||||||
|
}
|
||||||
|
if data.IsCat {
|
||||||
|
if !sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool {
|
||||||
|
return t.Being == string(models.BEING_CAT)
|
||||||
|
}) {
|
||||||
|
log.Debug().Msg("user doesn't contain cat yet")
|
||||||
|
bt := models.UserToBeing{UserId: user.ID, Being: string(models.BEING_CAT)}
|
||||||
|
if err = dbgen.UserToBeing.Create(&bt); err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Failed to append cat being type to imported user")
|
||||||
|
}
|
||||||
|
if err = dbgen.User.BeingTypes.Model(user).Append(&bt); err != nil {
|
||||||
|
log.Warn().
|
||||||
|
Err(err).
|
||||||
|
Msg("Failed to append cat being type to imported user relations")
|
||||||
|
}
|
||||||
|
user.BeingTypes = append(user.BeingTypes, bt)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool {
|
||||||
|
return t.Being == string(models.BEING_CAT)
|
||||||
|
}) {
|
||||||
|
_, err = dbgen.UserToBeing.Where(dbgen.UserToBeing.UserId.Eq(user.ID)).Where(dbgen.UserToBeing.Being.Eq(models.BEING_CAT)).Delete()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Failed to remove cat being type from user")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Don't handle SpeakAsCat yet, as not included in stored data yet
|
||||||
|
|
||||||
|
// Handle media last as more complicated
|
||||||
|
// Icon *inboundImportUserMedia `json:"icon,omitempty"`
|
||||||
|
// Banner *inboundImportUserMedia `json:"image,omitempty"`
|
||||||
|
|
||||||
|
id := user.ID
|
||||||
|
user.ID = ""
|
||||||
|
if _, err = dbgen.User.Where(dbgen.User.ID.Eq(id)).UpdateColumns(user); err != nil {
|
||||||
|
return "", other.Error("activitypub", "failed to update imported user", err)
|
||||||
|
}
|
||||||
|
rlid := user.RemoteInfo.ID
|
||||||
|
user.RemoteInfo.ID = 0
|
||||||
|
if _, err = dbgen.UserRemoteLinks.Where(dbgen.UserRemoteLinks.ID.Eq(rlid)).UpdateColumns(user.RemoteInfo); err != nil {
|
||||||
|
return "", other.Error("activitypub", "failed to update imported user's remote links", err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Info().Str("user", targetName).Msg("Import completed")
|
log.Info().Str("user", targetName).Msg("Import completed")
|
||||||
return "", nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImportRemoteServer(host string) (uint, error) {
|
func ImportRemoteServer(host string) (uint, error) {
|
||||||
|
|
|
@ -32,13 +32,13 @@ func newUserRemoteLinks(db *gorm.DB, opts ...gen.DOOption) userRemoteLinks {
|
||||||
_userRemoteLinks.DeletedAt = field.NewField(tableName, "deleted_at")
|
_userRemoteLinks.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||||
_userRemoteLinks.UserId = field.NewString(tableName, "user_id")
|
_userRemoteLinks.UserId = field.NewString(tableName, "user_id")
|
||||||
_userRemoteLinks.ApLink = field.NewString(tableName, "ap_link")
|
_userRemoteLinks.ApLink = field.NewString(tableName, "ap_link")
|
||||||
_userRemoteLinks.ViewLink = field.NewString(tableName, "view_link")
|
_userRemoteLinks.ViewLink = field.NewField(tableName, "view_link")
|
||||||
_userRemoteLinks.FollowersLink = field.NewString(tableName, "followers_link")
|
_userRemoteLinks.FollowersLink = field.NewField(tableName, "followers_link")
|
||||||
_userRemoteLinks.FollowingLink = field.NewString(tableName, "following_link")
|
_userRemoteLinks.FollowingLink = field.NewField(tableName, "following_link")
|
||||||
_userRemoteLinks.InboxLink = field.NewString(tableName, "inbox_link")
|
_userRemoteLinks.InboxLink = field.NewString(tableName, "inbox_link")
|
||||||
_userRemoteLinks.OutboxLink = field.NewString(tableName, "outbox_link")
|
_userRemoteLinks.OutboxLink = field.NewField(tableName, "outbox_link")
|
||||||
_userRemoteLinks.FeaturedLink = field.NewString(tableName, "featured_link")
|
_userRemoteLinks.FeaturedLink = field.NewField(tableName, "featured_link")
|
||||||
_userRemoteLinks.FeaturedTagsLink = field.NewString(tableName, "featured_tags_link")
|
_userRemoteLinks.FeaturedTagsLink = field.NewField(tableName, "featured_tags_link")
|
||||||
_userRemoteLinks.User = userRemoteLinksBelongsToUser{
|
_userRemoteLinks.User = userRemoteLinksBelongsToUser{
|
||||||
db: db.Session(&gorm.Session{}),
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
|
@ -227,13 +227,13 @@ type userRemoteLinks struct {
|
||||||
DeletedAt field.Field
|
DeletedAt field.Field
|
||||||
UserId field.String
|
UserId field.String
|
||||||
ApLink field.String
|
ApLink field.String
|
||||||
ViewLink field.String
|
ViewLink field.Field
|
||||||
FollowersLink field.String
|
FollowersLink field.Field
|
||||||
FollowingLink field.String
|
FollowingLink field.Field
|
||||||
InboxLink field.String
|
InboxLink field.String
|
||||||
OutboxLink field.String
|
OutboxLink field.Field
|
||||||
FeaturedLink field.String
|
FeaturedLink field.Field
|
||||||
FeaturedTagsLink field.String
|
FeaturedTagsLink field.Field
|
||||||
User userRemoteLinksBelongsToUser
|
User userRemoteLinksBelongsToUser
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
|
@ -257,13 +257,13 @@ func (u *userRemoteLinks) updateTableName(table string) *userRemoteLinks {
|
||||||
u.DeletedAt = field.NewField(table, "deleted_at")
|
u.DeletedAt = field.NewField(table, "deleted_at")
|
||||||
u.UserId = field.NewString(table, "user_id")
|
u.UserId = field.NewString(table, "user_id")
|
||||||
u.ApLink = field.NewString(table, "ap_link")
|
u.ApLink = field.NewString(table, "ap_link")
|
||||||
u.ViewLink = field.NewString(table, "view_link")
|
u.ViewLink = field.NewField(table, "view_link")
|
||||||
u.FollowersLink = field.NewString(table, "followers_link")
|
u.FollowersLink = field.NewField(table, "followers_link")
|
||||||
u.FollowingLink = field.NewString(table, "following_link")
|
u.FollowingLink = field.NewField(table, "following_link")
|
||||||
u.InboxLink = field.NewString(table, "inbox_link")
|
u.InboxLink = field.NewString(table, "inbox_link")
|
||||||
u.OutboxLink = field.NewString(table, "outbox_link")
|
u.OutboxLink = field.NewField(table, "outbox_link")
|
||||||
u.FeaturedLink = field.NewString(table, "featured_link")
|
u.FeaturedLink = field.NewField(table, "featured_link")
|
||||||
u.FeaturedTagsLink = field.NewString(table, "featured_tags_link")
|
u.FeaturedTagsLink = field.NewField(table, "featured_tags_link")
|
||||||
|
|
||||||
u.fillFieldMap()
|
u.fillFieldMap()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ func newUserToBeing(db *gorm.DB, opts ...gen.DOOption) userToBeing {
|
||||||
_userToBeing.ALL = field.NewAsterisk(tableName)
|
_userToBeing.ALL = field.NewAsterisk(tableName)
|
||||||
_userToBeing.ID = field.NewUint64(tableName, "id")
|
_userToBeing.ID = field.NewUint64(tableName, "id")
|
||||||
_userToBeing.UserId = field.NewString(tableName, "user_id")
|
_userToBeing.UserId = field.NewString(tableName, "user_id")
|
||||||
_userToBeing.Being = field.NewField(tableName, "being")
|
_userToBeing.Being = field.NewString(tableName, "being")
|
||||||
_userToBeing.User = userToBeingBelongsToUser{
|
_userToBeing.User = userToBeingBelongsToUser{
|
||||||
db: db.Session(&gorm.Session{}),
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ type userToBeing struct {
|
||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Uint64
|
ID field.Uint64
|
||||||
UserId field.String
|
UserId field.String
|
||||||
Being field.Field
|
Being field.String
|
||||||
User userToBeingBelongsToUser
|
User userToBeingBelongsToUser
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
|
@ -233,7 +233,7 @@ func (u *userToBeing) updateTableName(table string) *userToBeing {
|
||||||
u.ALL = field.NewAsterisk(table)
|
u.ALL = field.NewAsterisk(table)
|
||||||
u.ID = field.NewUint64(table, "id")
|
u.ID = field.NewUint64(table, "id")
|
||||||
u.UserId = field.NewString(table, "user_id")
|
u.UserId = field.NewString(table, "user_id")
|
||||||
u.Being = field.NewField(table, "being")
|
u.Being = field.NewString(table, "being")
|
||||||
|
|
||||||
u.fillFieldMap()
|
u.fillFieldMap()
|
||||||
|
|
||||||
|
|
|
@ -43,13 +43,14 @@ func newUser(db *gorm.DB, opts ...gen.DOOption) user {
|
||||||
_user.PublicKeyRsa = field.NewBytes(tableName, "public_key_rsa")
|
_user.PublicKeyRsa = field.NewBytes(tableName, "public_key_rsa")
|
||||||
_user.PublicKeyEd = field.NewBytes(tableName, "public_key_ed")
|
_user.PublicKeyEd = field.NewBytes(tableName, "public_key_ed")
|
||||||
_user.RestrictedFollow = field.NewBool(tableName, "restricted_follow")
|
_user.RestrictedFollow = field.NewBool(tableName, "restricted_follow")
|
||||||
_user.Location = field.NewField(tableName, "location")
|
|
||||||
_user.Birthday = field.NewField(tableName, "birthday")
|
_user.Birthday = field.NewField(tableName, "birthday")
|
||||||
|
_user.Location = field.NewField(tableName, "location")
|
||||||
_user.Verified = field.NewBool(tableName, "verified")
|
_user.Verified = field.NewBool(tableName, "verified")
|
||||||
_user.PasskeyId = field.NewBytes(tableName, "passkey_id")
|
_user.PasskeyId = field.NewBytes(tableName, "passkey_id")
|
||||||
_user.FinishedRegistration = field.NewBool(tableName, "finished_registration")
|
_user.FinishedRegistration = field.NewBool(tableName, "finished_registration")
|
||||||
_user.PrivateKeyRsa = field.NewBytes(tableName, "private_key_rsa")
|
_user.PrivateKeyRsa = field.NewBytes(tableName, "private_key_rsa")
|
||||||
_user.PrivateKeyEd = field.NewBytes(tableName, "private_key_ed")
|
_user.PrivateKeyEd = field.NewBytes(tableName, "private_key_ed")
|
||||||
|
_user.RemoteInfoId = field.NewField(tableName, "remote_info_id")
|
||||||
_user.RemoteInfo = userHasOneRemoteInfo{
|
_user.RemoteInfo = userHasOneRemoteInfo{
|
||||||
db: db.Session(&gorm.Session{}),
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
|
@ -383,13 +384,14 @@ type user struct {
|
||||||
PublicKeyRsa field.Bytes
|
PublicKeyRsa field.Bytes
|
||||||
PublicKeyEd field.Bytes
|
PublicKeyEd field.Bytes
|
||||||
RestrictedFollow field.Bool
|
RestrictedFollow field.Bool
|
||||||
Location field.Field
|
|
||||||
Birthday field.Field
|
Birthday field.Field
|
||||||
|
Location field.Field
|
||||||
Verified field.Bool
|
Verified field.Bool
|
||||||
PasskeyId field.Bytes
|
PasskeyId field.Bytes
|
||||||
FinishedRegistration field.Bool
|
FinishedRegistration field.Bool
|
||||||
PrivateKeyRsa field.Bytes
|
PrivateKeyRsa field.Bytes
|
||||||
PrivateKeyEd field.Bytes
|
PrivateKeyEd field.Bytes
|
||||||
|
RemoteInfoId field.Field
|
||||||
RemoteInfo userHasOneRemoteInfo
|
RemoteInfo userHasOneRemoteInfo
|
||||||
|
|
||||||
InfoFields userHasManyInfoFields
|
InfoFields userHasManyInfoFields
|
||||||
|
@ -445,13 +447,14 @@ func (u *user) updateTableName(table string) *user {
|
||||||
u.PublicKeyRsa = field.NewBytes(table, "public_key_rsa")
|
u.PublicKeyRsa = field.NewBytes(table, "public_key_rsa")
|
||||||
u.PublicKeyEd = field.NewBytes(table, "public_key_ed")
|
u.PublicKeyEd = field.NewBytes(table, "public_key_ed")
|
||||||
u.RestrictedFollow = field.NewBool(table, "restricted_follow")
|
u.RestrictedFollow = field.NewBool(table, "restricted_follow")
|
||||||
u.Location = field.NewField(table, "location")
|
|
||||||
u.Birthday = field.NewField(table, "birthday")
|
u.Birthday = field.NewField(table, "birthday")
|
||||||
|
u.Location = field.NewField(table, "location")
|
||||||
u.Verified = field.NewBool(table, "verified")
|
u.Verified = field.NewBool(table, "verified")
|
||||||
u.PasskeyId = field.NewBytes(table, "passkey_id")
|
u.PasskeyId = field.NewBytes(table, "passkey_id")
|
||||||
u.FinishedRegistration = field.NewBool(table, "finished_registration")
|
u.FinishedRegistration = field.NewBool(table, "finished_registration")
|
||||||
u.PrivateKeyRsa = field.NewBytes(table, "private_key_rsa")
|
u.PrivateKeyRsa = field.NewBytes(table, "private_key_rsa")
|
||||||
u.PrivateKeyEd = field.NewBytes(table, "private_key_ed")
|
u.PrivateKeyEd = field.NewBytes(table, "private_key_ed")
|
||||||
|
u.RemoteInfoId = field.NewField(table, "remote_info_id")
|
||||||
|
|
||||||
u.fillFieldMap()
|
u.fillFieldMap()
|
||||||
|
|
||||||
|
@ -468,7 +471,7 @@ func (u *user) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *user) fillFieldMap() {
|
func (u *user) fillFieldMap() {
|
||||||
u.fieldMap = make(map[string]field.Expr, 35)
|
u.fieldMap = make(map[string]field.Expr, 36)
|
||||||
u.fieldMap["id"] = u.ID
|
u.fieldMap["id"] = u.ID
|
||||||
u.fieldMap["username"] = u.Username
|
u.fieldMap["username"] = u.Username
|
||||||
u.fieldMap["created_at"] = u.CreatedAt
|
u.fieldMap["created_at"] = u.CreatedAt
|
||||||
|
@ -485,13 +488,14 @@ func (u *user) fillFieldMap() {
|
||||||
u.fieldMap["public_key_rsa"] = u.PublicKeyRsa
|
u.fieldMap["public_key_rsa"] = u.PublicKeyRsa
|
||||||
u.fieldMap["public_key_ed"] = u.PublicKeyEd
|
u.fieldMap["public_key_ed"] = u.PublicKeyEd
|
||||||
u.fieldMap["restricted_follow"] = u.RestrictedFollow
|
u.fieldMap["restricted_follow"] = u.RestrictedFollow
|
||||||
u.fieldMap["location"] = u.Location
|
|
||||||
u.fieldMap["birthday"] = u.Birthday
|
u.fieldMap["birthday"] = u.Birthday
|
||||||
|
u.fieldMap["location"] = u.Location
|
||||||
u.fieldMap["verified"] = u.Verified
|
u.fieldMap["verified"] = u.Verified
|
||||||
u.fieldMap["passkey_id"] = u.PasskeyId
|
u.fieldMap["passkey_id"] = u.PasskeyId
|
||||||
u.fieldMap["finished_registration"] = u.FinishedRegistration
|
u.fieldMap["finished_registration"] = u.FinishedRegistration
|
||||||
u.fieldMap["private_key_rsa"] = u.PrivateKeyRsa
|
u.fieldMap["private_key_rsa"] = u.PrivateKeyRsa
|
||||||
u.fieldMap["private_key_ed"] = u.PrivateKeyEd
|
u.fieldMap["private_key_ed"] = u.PrivateKeyEd
|
||||||
|
u.fieldMap["remote_info_id"] = u.RemoteInfoId
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,12 +55,14 @@ type User struct {
|
||||||
// If true, the owner must approve of a follow request first
|
// If true, the owner must approve of a follow request first
|
||||||
RestrictedFollow bool
|
RestrictedFollow bool
|
||||||
|
|
||||||
|
// Technically should be a timestamp, but can't trust other implementations
|
||||||
|
// to enforce this in a consistent format
|
||||||
|
Birthday sql.NullString
|
||||||
Location sql.NullString
|
Location sql.NullString
|
||||||
Birthday sql.NullTime
|
|
||||||
|
|
||||||
// Whether the account got verified and is allowed to be active
|
// Whether the account got verified and is allowed to be active
|
||||||
// For local accounts being active means being allowed to login and perform interactions
|
// For local accounts being active means being allowed to login and perform interactions
|
||||||
// For remote users, if an account is not verified, any interactions it sends are discarded
|
// No impact on remote accounts
|
||||||
Verified bool
|
Verified bool
|
||||||
// 64 byte unique id for passkeys, because UUIDs are 128 bytes and passkey spec says 64 bytes max
|
// 64 byte unique id for passkeys, because UUIDs are 128 bytes and passkey spec says 64 bytes max
|
||||||
// In theory, could also slash Id in half, but that would be a lot more calculations than the
|
// In theory, could also slash Id in half, but that would be a lot more calculations than the
|
||||||
|
@ -78,6 +80,7 @@ type User struct {
|
||||||
Pronouns []UserToPronoun
|
Pronouns []UserToPronoun
|
||||||
Roles []UserToRole
|
Roles []UserToRole
|
||||||
RemoteInfo *UserRemoteLinks
|
RemoteInfo *UserRemoteLinks
|
||||||
|
RemoteInfoId sql.NullInt64
|
||||||
AuthMethods []UserAuthMethod
|
AuthMethods []UserAuthMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ const (
|
||||||
var AllBeings = []BeingType{BEING_HUMAN, BEING_CAT, BEING_FOX, BEING_DOG, BEING_ROBOT, BEING_DOLL}
|
var AllBeings = []BeingType{BEING_HUMAN, BEING_CAT, BEING_FOX, BEING_DOG, BEING_ROBOT, BEING_DOLL}
|
||||||
|
|
||||||
func (ct *BeingType) Scan(value any) error {
|
func (ct *BeingType) Scan(value any) error {
|
||||||
*ct = BeingType(value.([]byte))
|
*ct = BeingType(value.(string))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,5 +8,5 @@ type UserToBeing struct {
|
||||||
ID uint64 `gorm:"primarykey"`
|
ID uint64 `gorm:"primarykey"`
|
||||||
User User
|
User User
|
||||||
UserId string
|
UserId string
|
||||||
Being BeingType `gorm:"type:being_type"`
|
Being string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "gorm.io/gorm"
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// UserRemoteLinks contains cached links for remote users
|
// UserRemoteLinks contains cached links for remote users
|
||||||
type UserRemoteLinks struct {
|
type UserRemoteLinks struct {
|
||||||
|
@ -13,11 +17,11 @@ type UserRemoteLinks struct {
|
||||||
// Just about every link here is optional to accomodate for servers with only minimal accounts
|
// Just about every link here is optional to accomodate for servers with only minimal accounts
|
||||||
// Minimal being handle, ap link and inbox
|
// Minimal being handle, ap link and inbox
|
||||||
ApLink string
|
ApLink string
|
||||||
ViewLink *string
|
ViewLink sql.NullString
|
||||||
FollowersLink *string
|
FollowersLink sql.NullString
|
||||||
FollowingLink *string
|
FollowingLink sql.NullString
|
||||||
InboxLink string
|
InboxLink string
|
||||||
OutboxLink *string
|
OutboxLink sql.NullString
|
||||||
FeaturedLink *string
|
FeaturedLink sql.NullString
|
||||||
FeaturedTagsLink *string
|
FeaturedTagsLink sql.NullString
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[general]
|
[general]
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
domain = "serveo.net"
|
domain = "lhr.life"
|
||||||
subdomain = "cc45720a387f04ba6a748a2627327a77"
|
subdomain = "47565bb39100de"
|
||||||
private_port = 8080
|
private_port = 8080
|
||||||
public_port = 443
|
public_port = 443
|
||||||
|
|
||||||
|
|
|
@ -158,12 +158,13 @@ func createLocalUser(w http.ResponseWriter, r *http.Request) {
|
||||||
PasskeyId: pkeyId,
|
PasskeyId: pkeyId,
|
||||||
}
|
}
|
||||||
if data.Birthday != nil {
|
if data.Birthday != nil {
|
||||||
user.Birthday = sql.NullTime{Valid: true, Time: *data.Birthday}
|
user.Birthday = sql.NullString{Valid: true, String: data.Birthday.Format("2006-Jan-02")}
|
||||||
|
// user.Birthday = sql.NullTime{Valid: true, Time: *data.Birthday}
|
||||||
}
|
}
|
||||||
if data.Location != nil {
|
if data.Location != nil {
|
||||||
user.Location = sql.NullString{Valid: true, String: *data.Location}
|
user.Location = sql.NullString{Valid: true, String: *data.Location}
|
||||||
}
|
}
|
||||||
if err = u.Create(&user); err != nil {
|
if err = query.Create(&user); err != nil {
|
||||||
log.Error().Err(err).Msg("failed to create new local user")
|
log.Error().Err(err).Msg("failed to create new local user")
|
||||||
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
webutils "git.mstar.dev/mstar/goutils/http"
|
webutils "git.mstar.dev/mstar/goutils/http"
|
||||||
"git.mstar.dev/mstar/goutils/other"
|
|
||||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||||
"github.com/rs/zerolog/hlog"
|
"github.com/rs/zerolog/hlog"
|
||||||
|
|
||||||
|
@ -119,7 +118,7 @@ func users(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool {
|
if sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool {
|
||||||
return t.Being == models.BEING_CAT
|
return t.Being == string(models.BEING_CAT)
|
||||||
}) {
|
}) {
|
||||||
data.IsCat = true
|
data.IsCat = true
|
||||||
// data.SpeakAsCat = true // TODO: Move to check of separate field in db model
|
// data.SpeakAsCat = true // TODO: Move to check of separate field in db model
|
||||||
|
@ -128,10 +127,16 @@ func users(w http.ResponseWriter, r *http.Request) {
|
||||||
data.Location = &user.Location.String
|
data.Location = &user.Location.String
|
||||||
}
|
}
|
||||||
if user.Birthday.Valid {
|
if user.Birthday.Valid {
|
||||||
data.Birthday = other.IntoPointer(user.Birthday.Time.Format("2006-Jan-02")) //YYYY-Month-DD
|
data.Birthday = &user.Birthday.String
|
||||||
|
// data.Birthday = other.IntoPointer(user.Birthday.Time.Format("2006-Jan-02")) //YYYY-Month-DD
|
||||||
}
|
}
|
||||||
|
|
||||||
encoded, err := json.Marshal(data)
|
encoded, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to marshal response")
|
||||||
|
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
w.Header().Add("Content-Type", "application/activity+json")
|
w.Header().Add("Content-Type", "application/activity+json")
|
||||||
fmt.Fprint(w, string(encoded))
|
fmt.Fprint(w, string(encoded))
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ type User struct {
|
||||||
PublicKey []byte `json:"public_key"`
|
PublicKey []byte `json:"public_key"`
|
||||||
RestrictedFollow bool `json:"restricted_follow"`
|
RestrictedFollow bool `json:"restricted_follow"`
|
||||||
Location *string `json:"location"`
|
Location *string `json:"location"`
|
||||||
Birthday *time.Time `json:"birthday"`
|
Birthday *string `json:"birthday"`
|
||||||
|
|
||||||
// ---- Section Debug data ----
|
// ---- Section Debug data ----
|
||||||
// All these entries should only be available
|
// All these entries should only be available
|
||||||
|
@ -113,7 +113,7 @@ func (u *User) FromModel(m *models.User) {
|
||||||
u.Location = &m.Location.String
|
u.Location = &m.Location.String
|
||||||
}
|
}
|
||||||
if m.Birthday.Valid {
|
if m.Birthday.Valid {
|
||||||
u.Birthday = &m.Birthday.Time
|
u.Birthday = &m.Birthday.String
|
||||||
}
|
}
|
||||||
u.Verified = &m.Verified
|
u.Verified = &m.Verified
|
||||||
u.FinishedRegistration = &m.FinishedRegistration
|
u.FinishedRegistration = &m.FinishedRegistration
|
||||||
|
|
Loading…
Reference in a new issue