🎨 Update project structure

This commit is contained in:
2024-06-16 23:17:32 +08:00
parent 0695338fa1
commit 45048ea814
103 changed files with 138 additions and 40 deletions

View File

@ -0,0 +1,80 @@
package models
import (
"fmt"
"time"
"github.com/samber/lo"
"github.com/spf13/viper"
"gorm.io/datatypes"
)
type Account struct {
BaseModel
Name string `json:"name" gorm:"uniqueIndex"`
Nick string `json:"nick"`
Description string `json:"description"`
Avatar *uint `json:"avatar"`
Banner *uint `json:"banner"`
ConfirmedAt *time.Time `json:"confirmed_at"`
PermNodes datatypes.JSONMap `json:"perm_nodes"`
Profile AccountProfile `json:"profile"`
PersonalPage AccountPage `json:"personal_page"`
Badges []Badge `json:"badges"`
Contacts []AccountContact `json:"contacts"`
RealmIdentities []RealmMember `json:"realm_identities"`
Tickets []AuthTicket `json:"tickets"`
Factors []AuthFactor `json:"factors"`
Events []ActionEvent `json:"events"`
MagicTokens []MagicToken `json:"-" gorm:"foreignKey:AssignTo"`
ThirdClients []ThirdClient `json:"clients"`
Notifications []Notification `json:"notifications" gorm:"foreignKey:RecipientID"`
NotifySubscribers []NotificationSubscriber `json:"notify_subscribers"`
Friendships []AccountFriendship `json:"friendships" gorm:"foreignKey:AccountID"`
RelatedFriendships []AccountFriendship `json:"related_friendships" gorm:"foreignKey:RelatedID"`
}
func (v Account) GetAvatar() *string {
if v.Avatar != nil {
return lo.ToPtr(fmt.Sprintf("%s/api/attachments/%d", viper.GetString("paperclip.endpoint"), *v.Avatar))
}
return nil
}
func (v Account) GetBanner() *string {
if v.Banner != nil {
return lo.ToPtr(fmt.Sprintf("%s/api/attachments/%d", viper.GetString("paperclip.endpoint"), *v.Banner))
}
return nil
}
func (v Account) GetPrimaryEmail() AccountContact {
val, _ := lo.Find(v.Contacts, func(item AccountContact) bool {
return item.Type == EmailAccountContact && item.IsPrimary
})
return val
}
type AccountContactType = int8
const (
EmailAccountContact = AccountContactType(iota)
)
type AccountContact struct {
BaseModel
Type int8 `json:"type"`
Content string `json:"content" gorm:"uniqueIndex"`
IsPublic bool `json:"is_public"`
IsPrimary bool `json:"is_primary"`
VerifiedAt *time.Time `json:"verified_at"`
AccountID uint `json:"account_id"`
}

View File

@ -0,0 +1,64 @@
package models
import (
"fmt"
"time"
"gorm.io/datatypes"
)
type AuthFactorType = int8
const (
PasswordAuthFactor = AuthFactorType(iota)
EmailPasswordFactor
)
type AuthFactor struct {
BaseModel
Type int8 `json:"type"`
Secret string `json:"-"`
Config JSONMap `json:"config"`
AccountID uint `json:"account_id"`
}
type AuthTicket struct {
BaseModel
Location string `json:"location"`
IpAddress string `json:"ip_address"`
UserAgent string `json:"user_agent"`
RequireMFA bool `json:"require_mfa"`
RequireAuthenticate bool `json:"require_authenticate"`
Claims datatypes.JSONSlice[string] `json:"claims"`
Audiences datatypes.JSONSlice[string] `json:"audiences"`
GrantToken *string `json:"grant_token"`
AccessToken *string `json:"access_token"`
RefreshToken *string `json:"refresh_token"`
ExpiredAt *time.Time `json:"expired_at"`
AvailableAt *time.Time `json:"available_at"`
LastGrantAt *time.Time `json:"last_grant_at"`
ClientID *uint `json:"client_id"`
AccountID uint `json:"account_id"`
}
func (v AuthTicket) IsAvailable() error {
if v.RequireMFA || v.RequireAuthenticate {
return fmt.Errorf("ticket isn't authenticated yet")
}
if v.AvailableAt != nil && time.Now().Unix() < v.AvailableAt.Unix() {
return fmt.Errorf("ticket isn't available yet")
}
if v.ExpiredAt != nil && time.Now().Unix() > v.ExpiredAt.Unix() {
return fmt.Errorf("ticket expired")
}
return nil
}
type AuthContext struct {
Ticket AuthTicket `json:"ticket"`
Account Account `json:"account"`
LastUsedAt time.Time `json:"last_used_at"`
}

View File

@ -0,0 +1,11 @@
package models
import "gorm.io/datatypes"
type Badge struct {
BaseModel
Type string `json:"type"`
Metadata datatypes.JSONMap `json:"metadata"`
AccountID uint `json:"account_id"`
}

View File

@ -0,0 +1,17 @@
package models
import (
"time"
"gorm.io/datatypes"
"gorm.io/gorm"
)
type JSONMap = datatypes.JSONType[map[string]any]
type BaseModel struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
}

View File

@ -0,0 +1,18 @@
package models
import "gorm.io/datatypes"
type ThirdClient struct {
BaseModel
Alias string `json:"alias" gorm:"uniqueIndex"`
Name string `json:"name"`
Description string `json:"description"`
Secret string `json:"secret"`
Urls datatypes.JSONSlice[string] `json:"urls"`
Callbacks datatypes.JSONSlice[string] `json:"callbacks"`
Sessions []AuthTicket `json:"tickets" gorm:"foreignKey:ClientID"`
Notifications []Notification `json:"notifications" gorm:"foreignKey:SenderID"`
IsDraft bool `json:"is_draft"`
AccountID *uint `json:"account_id"`
}

View File

@ -0,0 +1,12 @@
package models
type ActionEvent struct {
BaseModel
Type string `json:"type"`
Target string `json:"target"`
Location string `json:"location"`
IpAddress string `json:"ip_address"`
UserAgent string `json:"user_agent"`
AccountID uint `json:"account_id"`
}

View File

@ -0,0 +1,20 @@
package models
type FriendshipStatus = int8
const (
FriendshipPending = FriendshipStatus(iota)
FriendshipActive
FriendshipBlocked
)
type AccountFriendship struct {
BaseModel
AccountID uint `json:"account_id"`
RelatedID uint `json:"related_id"`
BlockedBy *uint `json:"blocked_by"`
Account Account `json:"account"`
Related Account `json:"related"`
Status FriendshipStatus `json:"status"`
}

View File

@ -0,0 +1,40 @@
package models
import (
"gorm.io/datatypes"
)
type Notification struct {
BaseModel
Type string `json:"type"`
Subject string `json:"subject"`
Content string `json:"content"`
Metadata datatypes.JSONMap `json:"metadata"`
Links datatypes.JSONSlice[NotificationLink] `json:"links"`
IsRealtime bool `json:"is_realtime" gorm:"-"`
IsForcePush bool `json:"is_force_push" gorm:"-"`
SenderID *uint `json:"sender_id"`
RecipientID uint `json:"recipient_id"`
}
// NotificationLink Used to embed into notify and render actions
type NotificationLink struct {
Label string `json:"label"`
Url string `json:"url"`
}
const (
NotifySubscriberFirebase = "firebase"
NotifySubscriberAPNs = "apple"
)
type NotificationSubscriber struct {
BaseModel
UserAgent string `json:"user_agent"`
Provider string `json:"provider"`
DeviceID string `json:"device_id" gorm:"uniqueIndex"`
DeviceToken string `json:"device_token"`
AccountID uint `json:"account_id"`
}

View File

@ -0,0 +1,31 @@
package models
import (
"gorm.io/datatypes"
"time"
)
type AccountProfile struct {
BaseModel
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Experience uint64 `json:"experience"`
Birthday *time.Time `json:"birthday"`
AccountID uint `json:"account_id"`
}
type AccountPage struct {
BaseModel
Content string `json:"content"`
Script string `json:"script"`
Style string `json:"style"`
Links datatypes.JSONSlice[AccountPageLinks] `json:"links"`
AccountID uint `json:"account_id"`
}
type AccountPageLinks struct {
Label string `json:"label"`
Url string `json:"url"`
}

View File

@ -0,0 +1,23 @@
package models
type Realm struct {
BaseModel
Alias string `json:"alias" gorm:"uniqueIndex"`
Name string `json:"name"`
Description string `json:"description"`
Members []RealmMember `json:"members"`
IsPublic bool `json:"is_public"`
IsCommunity bool `json:"is_community"`
AccountID uint `json:"account_id"`
}
type RealmMember struct {
BaseModel
RealmID uint `json:"realm_id"`
AccountID uint `json:"account_id"`
Realm Realm `json:"realm"`
Account Account `json:"account"`
PowerLevel int `json:"power_level"`
}

View File

@ -0,0 +1,19 @@
package models
import "time"
type MagicTokenType = int8
const (
ConfirmMagicToken = MagicTokenType(iota)
RegistrationMagicToken
)
type MagicToken struct {
BaseModel
Code string `json:"code"`
Type int8 `json:"type"`
AssignTo *uint `json:"assign_to"`
ExpiredAt *time.Time `json:"expired_at"`
}

View File

@ -0,0 +1,21 @@
package models
import jsoniter "github.com/json-iterator/go"
type UnifiedCommand struct {
Action string `json:"w"`
Message string `json:"m"`
Payload any `json:"p"`
}
func UnifiedCommandFromError(err error) UnifiedCommand {
return UnifiedCommand{
Action: "error",
Message: err.Error(),
}
}
func (v UnifiedCommand) Marshal() []byte {
data, _ := jsoniter.Marshal(v)
return data
}