User import now works
Some checks failed
/ docker (push) Failing after 3m56s

This commit is contained in:
Melody Becker 2025-04-15 17:18:56 +02:00
parent 08f6de0bd7
commit 5e93ecee73
Signed by: mstar
SSH key fingerprint: SHA256:9VAo09aaVNTWKzPW7Hq2LW+ox9OdwmTSHRoD4mlz1yI
12 changed files with 241 additions and 109 deletions

View file

@ -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) {

View file

@ -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()

View file

@ -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()

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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

View file

@ -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)
} }

View file

@ -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))
} }

View file

@ -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