[chore] Remove go-playground/validator (#2069)

* [chore] Remove go-playground/validator

It turns out we're not actually using the validator code. This is a
remnant from when we intended to use it, but the presence of it and its
struct tags creates the illusion we're validating a lot of things we're
not. It resulted in some confusion when we were trying to figure out
language valdiation.

Remove all this code, so that only the validation functions from the
validate package we actually use remain. I'm not touching the struct
tags in the migrations in order to avoid things potentially thinking
migrations need to be re-run.

* [chore] Bring back a struct tag on api

The validate on internal/api is Gin doing form validation, not the
validator from go-playground/validator.
This commit is contained in:
Daenney 2023-08-06 12:22:40 +02:00 committed by GitHub
parent 6f4ae8f58d
commit c1375ca5c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 423 additions and 3164 deletions

2
go.mod
View file

@ -28,7 +28,6 @@ require (
github.com/gin-gonic/gin v1.9.1
github.com/go-fed/httpsig v1.1.0
github.com/go-playground/form/v4 v4.2.1
github.com/go-playground/validator/v10 v10.14.1
github.com/google/uuid v1.3.0
github.com/gorilla/feeds v1.1.1
github.com/gorilla/websocket v1.5.0
@ -108,6 +107,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.1 // indirect
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/godbus/dbus/v5 v5.0.4 // indirect

View file

@ -32,55 +32,55 @@ import (
// Account represents either a local or a remote fediverse account, gotosocial or otherwise (mastodon, pleroma, etc).
type Account struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created.
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item was last updated.
FetchedAt time.Time `validate:"required_with=Domain" bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched.
Username string `validate:"required" bun:",nullzero,notnull,unique:usernamedomain"` // Username of the account, should just be a string of [a-zA-Z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``. Username and domain should be unique *with* each other
Domain string `validate:"omitempty,fqdn" bun:",nullzero,unique:usernamedomain"` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org``. Should be unique with username.
AvatarMediaAttachmentID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present
AvatarMediaAttachment *MediaAttachment `validate:"-" bun:"rel:belongs-to"` // MediaAttachment corresponding to avatarMediaAttachmentID
AvatarRemoteURL string `validate:"omitempty,url" bun:",nullzero"` // For a non-local account, where can the header be fetched?
HeaderMediaAttachmentID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present
HeaderMediaAttachment *MediaAttachment `validate:"-" bun:"rel:belongs-to"` // MediaAttachment corresponding to headerMediaAttachmentID
HeaderRemoteURL string `validate:"omitempty,url" bun:",nullzero"` // For a non-local account, where can the header be fetched?
DisplayName string `validate:"-" bun:""` // DisplayName for this account. Can be empty, then just the Username will be used for display purposes.
EmojiIDs []string `validate:"dive,ulid" bun:"emojis,array"` // Database IDs of any emojis used in this account's bio, display name, etc
Emojis []*Emoji `validate:"-" bun:"attached_emojis,m2m:account_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
Fields []*Field `validate:"-"` // A slice of of fields that this account has added to their profile.
FieldsRaw []*Field `validate:"-"` // The raw (unparsed) content of fields that this account has added to their profile, without conversion to HTML, only available when requester = target
Note string `validate:"-" bun:""` // A note that this account has on their profile (ie., the account's bio/description of themselves)
NoteRaw string `validate:"-" bun:""` // The raw contents of .Note without conversion to HTML, only available when requester = target
Memorial *bool `validate:"-" bun:",default:false"` // Is this a memorial account, ie., has the user passed away?
AlsoKnownAs string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // This account is associated with x account id (TODO: migrate to be AlsoKnownAsID)
MovedToAccountID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // This account has moved this account id in the database
Bot *bool `validate:"-" bun:",default:false"` // Does this account identify itself as a bot?
Reason string `validate:"-" bun:""` // What reason was given for signing up when this account was created?
Locked *bool `validate:"-" bun:",default:true"` // Does this account need an approval for new followers?
Discoverable *bool `validate:"-" bun:",default:false"` // Should this account be shown in the instance's profile directory?
Privacy Visibility `validate:"required_without=Domain,omitempty,oneof=public unlocked followers_only mutuals_only direct" bun:",nullzero"` // Default post privacy for this account
Sensitive *bool `validate:"-" bun:",default:false"` // Set posts from this account to sensitive by default?
Language string `validate:"omitempty,bcp47_language_tag" bun:",nullzero,notnull,default:'en'"` // What language does this account post in?
StatusContentType string `validate:"required_without=Domain,omitempty,oneof=text/plain text/markdown" bun:",nullzero"` // What is the default format for statuses posted by this account (only for local accounts).
CustomCSS string `validate:"-" bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses.
URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // ActivityPub URI for this account.
URL string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // Web URL for this account's profile
InboxURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // Address of this account's ActivityPub inbox, for sending activity to
SharedInboxURI *string `validate:"-" bun:""` // Address of this account's ActivityPub sharedInbox. Gotcha warning: this is a string pointer because it has three possible states: 1. We don't know yet if the account has a shared inbox -- null. 2. We know it doesn't have a shared inbox -- empty string. 3. We know it does have a shared inbox -- url string.
OutboxURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // Address of this account's activitypub outbox
FollowingURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // URI for getting the following list of this account
FollowersURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // URI for getting the followers list of this account
FeaturedCollectionURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // URL for getting the featured collection list of this account
ActorType string `validate:"oneof=Application Group Organization Person Service" bun:",nullzero,notnull"` // What type of activitypub actor is this account?
PrivateKey *rsa.PrivateKey `validate:"required_without=Domain" bun:""` // Privatekey for validating activitypub requests, will only be defined for local accounts
PublicKey *rsa.PublicKey `validate:"required" bun:",notnull"` // Publickey for encoding activitypub requests, will be defined for both local and remote accounts
PublicKeyURI string `validate:"required,url" bun:",nullzero,notnull,unique"` // Web-reachable location of this account's public key
SensitizedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account set to have all its media shown as sensitive?
SilencedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account silenced (eg., statuses only visible to followers, not public)?
SuspendedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account)
HideCollections *bool `validate:"-" bun:",default:false"` // Hide this account's collections
SuspensionOrigin string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the database entry that caused this account to become suspended -- can be an account ID or a domain block ID
EnableRSS *bool `validate:"-" bun:",default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created.
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item was last updated.
FetchedAt time.Time `bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched.
Username string `bun:",nullzero,notnull,unique:usernamedomain"` // Username of the account, should just be a string of [a-zA-Z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``. Username and domain should be unique *with* each other
Domain string `bun:",nullzero,unique:usernamedomain"` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org``. Should be unique with username.
AvatarMediaAttachmentID string `bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present
AvatarMediaAttachment *MediaAttachment `bun:"rel:belongs-to"` // MediaAttachment corresponding to avatarMediaAttachmentID
AvatarRemoteURL string `bun:",nullzero"` // For a non-local account, where can the header be fetched?
HeaderMediaAttachmentID string `bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present
HeaderMediaAttachment *MediaAttachment `bun:"rel:belongs-to"` // MediaAttachment corresponding to headerMediaAttachmentID
HeaderRemoteURL string `bun:",nullzero"` // For a non-local account, where can the header be fetched?
DisplayName string `bun:""` // DisplayName for this account. Can be empty, then just the Username will be used for display purposes.
EmojiIDs []string `bun:"emojis,array"` // Database IDs of any emojis used in this account's bio, display name, etc
Emojis []*Emoji `bun:"attached_emojis,m2m:account_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
Fields []*Field // A slice of of fields that this account has added to their profile.
FieldsRaw []*Field // The raw (unparsed) content of fields that this account has added to their profile, without conversion to HTML, only available when requester = target
Note string `bun:""` // A note that this account has on their profile (ie., the account's bio/description of themselves)
NoteRaw string `bun:""` // The raw contents of .Note without conversion to HTML, only available when requester = target
Memorial *bool `bun:",default:false"` // Is this a memorial account, ie., has the user passed away?
AlsoKnownAs string `bun:"type:CHAR(26),nullzero"` // This account is associated with x account id (TODO: migrate to be AlsoKnownAsID)
MovedToAccountID string `bun:"type:CHAR(26),nullzero"` // This account has moved this account id in the database
Bot *bool `bun:",default:false"` // Does this account identify itself as a bot?
Reason string `bun:""` // What reason was given for signing up when this account was created?
Locked *bool `bun:",default:true"` // Does this account need an approval for new followers?
Discoverable *bool `bun:",default:false"` // Should this account be shown in the instance's profile directory?
Privacy Visibility `bun:",nullzero"` // Default post privacy for this account
Sensitive *bool `bun:",default:false"` // Set posts from this account to sensitive by default?
Language string `bun:",nullzero,notnull,default:'en'"` // What language does this account post in?
StatusContentType string `bun:",nullzero"` // What is the default format for statuses posted by this account (only for local accounts).
CustomCSS string `bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses.
URI string `bun:",nullzero,notnull,unique"` // ActivityPub URI for this account.
URL string `bun:",nullzero,unique"` // Web URL for this account's profile
InboxURI string `bun:",nullzero,unique"` // Address of this account's ActivityPub inbox, for sending activity to
SharedInboxURI *string `bun:""` // Address of this account's ActivityPub sharedInbox. Gotcha warning: this is a string pointer because it has three possible states: 1. We don't know yet if the account has a shared inbox -- null. 2. We know it doesn't have a shared inbox -- empty string. 3. We know it does have a shared inbox -- url string.
OutboxURI string `bun:",nullzero,unique"` // Address of this account's activitypub outbox
FollowingURI string `bun:",nullzero,unique"` // URI for getting the following list of this account
FollowersURI string `bun:",nullzero,unique"` // URI for getting the followers list of this account
FeaturedCollectionURI string `bun:",nullzero,unique"` // URL for getting the featured collection list of this account
ActorType string `bun:",nullzero,notnull"` // What type of activitypub actor is this account?
PrivateKey *rsa.PrivateKey `bun:""` // Privatekey for validating activitypub requests, will only be defined for local accounts
PublicKey *rsa.PublicKey `bun:",notnull"` // Publickey for encoding activitypub requests, will be defined for both local and remote accounts
PublicKeyURI string `bun:",nullzero,notnull,unique"` // Web-reachable location of this account's public key
SensitizedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this account set to have all its media shown as sensitive?
SilencedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this account silenced (eg., statuses only visible to followers, not public)?
SuspendedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account)
HideCollections *bool `bun:",default:false"` // Hide this account's collections
SuspensionOrigin string `bun:"type:CHAR(26),nullzero"` // id of the database entry that caused this account to become suspended -- can be an account ID or a domain block ID
EnableRSS *bool `bun:",default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed
}
// IsLocal returns whether account is a local user account.
@ -131,19 +131,19 @@ func (a *Account) EmojisPopulated() bool {
// AccountToEmoji is an intermediate struct to facilitate the many2many relationship between an account and one or more emojis.
type AccountToEmoji struct {
AccountID string `validate:"ulid,required" bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull"`
Account *Account `validate:"-" bun:"rel:belongs-to"`
EmojiID string `validate:"ulid,required" bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull"`
Emoji *Emoji `validate:"-" bun:"rel:belongs-to"`
AccountID string `bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull"`
Account *Account `bun:"rel:belongs-to"`
EmojiID string `bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull"`
Emoji *Emoji `bun:"rel:belongs-to"`
}
// Field represents a key value field on an account, for things like pronouns, website, etc.
// VerifiedAt is optional, to be used only if Value is a URL to a webpage that contains the
// username of the user.
type Field struct {
Name string `validate:"required"` // Name of this field.
Value string `validate:"required"` // Value of this field.
VerifiedAt time.Time `validate:"-" bun:",nullzero"` // This field was verified at (optional).
Name string // Name of this field.
Value string // Value of this field.
VerifiedAt time.Time `bun:",nullzero"` // This field was verified at (optional).
}
// Relationship describes a requester's relationship with another account.

View file

@ -21,12 +21,12 @@ import "time"
// AccountNote stores a private note from a local account related to any account.
type AccountNote struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:account_notes_account_id_target_account_id_uniq,notnull,nullzero"` // ID of the local account that created the note
Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:account_notes_account_id_target_account_id_uniq,notnull,nullzero"` // Who is the target of this note?
TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID
Comment string `validate:"-" bun:""` // The text of the note.
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `bun:"type:CHAR(26),unique:account_notes_account_id_target_account_id_uniq,notnull,nullzero"` // ID of the local account that created the note
Account *Account `bun:"rel:belongs-to"` // Account corresponding to accountID
TargetAccountID string `bun:"type:CHAR(26),unique:account_notes_account_id_target_account_id_uniq,notnull,nullzero"` // Who is the target of this note?
TargetAccount *Account `bun:"rel:belongs-to"` // Account corresponding to targetAccountID
Comment string `bun:""` // The text of the note.
}

View file

@ -24,17 +24,17 @@ import (
// AdminAccountAction models an action taken by an instance administrator on an account.
type AdminAccountAction struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero"` // Who performed this admin action.
Account *Account `validate:"-" bun:"rel:has-one"` // Account corresponding to accountID
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero"` // Who is the target of this action
TargetAccount *Account `validate:"-" bun:"rel:has-one"` // Account corresponding to targetAccountID
Text string `validate:"-" bun:""` // text explaining why this action was taken
Type AdminActionType `validate:"oneof=disable silence suspend" bun:",nullzero,notnull"` // type of action that was taken
SendEmail bool `validate:"-" bun:""` // should an email be sent to the account owner to explain what happened
ReportID string `validate:",omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of a report connected to this action, if it exists
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `bun:"type:CHAR(26),notnull,nullzero"` // Who performed this admin action.
Account *Account `bun:"rel:has-one"` // Account corresponding to accountID
TargetAccountID string `bun:"type:CHAR(26),notnull,nullzero"` // Who is the target of this action
TargetAccount *Account `bun:"rel:has-one"` // Account corresponding to targetAccountID
Text string `bun:""` // text explaining why this action was taken
Type AdminActionType `bun:",nullzero,notnull"` // type of action that was taken
SendEmail bool `bun:""` // should an email be sent to the account owner to explain what happened
ReportID string `bun:"type:CHAR(26),nullzero"` // id of a report connected to this action, if it exists
}
// AdminActionType describes a type of action taken on an entity by an admin

View file

@ -22,13 +22,13 @@ import "time"
// Application represents an application that can perform actions on behalf of a user.
// It is used to authorize tokens etc, and is associated with an oauth client id in the database.
type Application struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Name string `validate:"required" bun:",notnull"` // name of the application given when it was created (eg., 'tusky')
Website string `validate:"omitempty,url" bun:",nullzero"` // website for the application given when it was created (eg., 'https://tusky.app')
RedirectURI string `validate:"required,uri" bun:",nullzero,notnull"` // redirect uri requested by the application for oauth2 flow
ClientID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id of the associated oauth client entity in the db
ClientSecret string `validate:"required,uuid" bun:",nullzero,notnull"` // secret of the associated oauth client entity in the db
Scopes string `validate:"required" bun:",notnull"` // scopes requested when this app was created
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Name string `bun:",notnull"` // name of the application given when it was created (eg., 'tusky')
Website string `bun:",nullzero"` // website for the application given when it was created (eg., 'https://tusky.app')
RedirectURI string `bun:",nullzero,notnull"` // redirect uri requested by the application for oauth2 flow
ClientID string `bun:"type:CHAR(26),nullzero,notnull"` // id of the associated oauth client entity in the db
ClientSecret string `bun:",nullzero,notnull"` // secret of the associated oauth client entity in the db
Scopes string `bun:",notnull"` // scopes requested when this app was created
}

View file

@ -21,12 +21,12 @@ import "time"
// Block refers to the blocking of one account by another.
type Block struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
URI string `validate:"required,url" bun:",notnull,nullzero,unique"` // ActivityPub uri of this block.
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:blocksrctarget,notnull,nullzero"` // Who does this block originate from?
Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:blocksrctarget,notnull,nullzero"` // Who is the target of this block ?
TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
URI string `bun:",notnull,nullzero,unique"` // ActivityPub uri of this block.
AccountID string `bun:"type:CHAR(26),unique:blocksrctarget,notnull,nullzero"` // Who does this block originate from?
Account *Account `bun:"rel:belongs-to"` // Account corresponding to accountID
TargetAccountID string `bun:"type:CHAR(26),unique:blocksrctarget,notnull,nullzero"` // Who is the target of this block ?
TargetAccount *Account `bun:"rel:belongs-to"` // Account corresponding to targetAccountID
}

View file

@ -21,10 +21,10 @@ import "time"
// Client is a wrapper for OAuth client details.
type Client struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Secret string `validate:"required,uuid" bun:",nullzero,notnull"` // secret generated when client was created
Domain string `validate:"required,uri" bun:",nullzero,notnull"` // domain requested for client
UserID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the user that this client acts on behalf of
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Secret string `bun:",nullzero,notnull"` // secret generated when client was created
Domain string `bun:",nullzero,notnull"` // domain requested for client
UserID string `bun:"type:CHAR(26),nullzero"` // id of the user that this client acts on behalf of
}

View file

@ -21,14 +21,14 @@ import "time"
// DomainBlock represents a federation block against a particular domain
type DomainBlock struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Domain string `validate:"required,fqdn" bun:",nullzero,notnull"` // domain to block. Eg. 'whatever.com'
CreatedByAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this block
CreatedByAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to createdByAccountID
PrivateComment string `validate:"-" bun:""` // Private comment on this block, viewable to admins
PublicComment string `validate:"-" bun:""` // Public comment on this block, viewable (optionally) by everyone
Obfuscate *bool `validate:"-" bun:",nullzero,notnull,default:false"` // whether the domain name should appear obfuscated when displaying it publicly
SubscriptionID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // if this block was created through a subscription, what's the subscription ID?
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Domain string `bun:",nullzero,notnull"` // domain to block. Eg. 'whatever.com'
CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this block
CreatedByAccount *Account `bun:"rel:belongs-to"` // Account corresponding to createdByAccountID
PrivateComment string `bun:""` // Private comment on this block, viewable to admins
PublicComment string `bun:""` // Public comment on this block, viewable (optionally) by everyone
Obfuscate *bool `bun:",nullzero,notnull,default:false"` // whether the domain name should appear obfuscated when displaying it publicly
SubscriptionID string `bun:"type:CHAR(26),nullzero"` // if this block was created through a subscription, what's the subscription ID?
}

View file

@ -21,10 +21,10 @@ import "time"
// EmailDomainBlock represents a domain that the server should automatically reject sign-up requests from.
type EmailDomainBlock struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Domain string `validate:"required,fqdn" bun:",nullzero,notnull"` // Email domain to block. Eg. 'gmail.com' or 'hotmail.com'
CreatedByAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this block
CreatedByAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to createdByAccountID
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Domain string `bun:",nullzero,notnull"` // Email domain to block. Eg. 'gmail.com' or 'hotmail.com'
CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this block
CreatedByAccount *Account `bun:"rel:belongs-to"` // Account corresponding to createdByAccountID
}

View file

@ -21,26 +21,26 @@ import "time"
// Emoji represents a custom emoji that's been uploaded through the admin UI or downloaded from a remote instance.
type Emoji struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Shortcode string `validate:"required" bun:",nullzero,notnull,unique:domainshortcode"` // String shortcode for this emoji -- the part that's between colons. This should be a-zA-Z_ eg., 'blob_hug' 'purple_heart' 'Gay_Otter' Must be unique with domain.
Domain string `validate:"omitempty,fqdn" bun:",nullzero,unique:domainshortcode"` // Origin domain of this emoji, eg 'example.org', 'queer.party'. empty string for local emojis.
ImageRemoteURL string `validate:"required_without=ImageURL,omitempty,url" bun:",nullzero"` // Where can this emoji be retrieved remotely? Null for local emojis.
ImageStaticRemoteURL string `validate:"required_without=ImageStaticURL,omitempty,url" bun:",nullzero"` // Where can a static / non-animated version of this emoji be retrieved remotely? Null for local emojis.
ImageURL string `validate:"required_without=ImageRemoteURL,required_without=Domain,omitempty,url" bun:",nullzero"` // Where can this emoji be retrieved from the local server? Null for remote emojis.
ImageStaticURL string `validate:"required_without=ImageStaticRemoteURL,required_without=Domain,omitempty,url" bun:",nullzero"` // Where can a static version of this emoji be retrieved from the local server? Null for remote emojis.
ImagePath string `validate:"required,file" bun:",nullzero,notnull"` // Path of the emoji image in the server storage system.
ImageStaticPath string `validate:"required,file" bun:",nullzero,notnull"` // Path of a static version of the emoji image in the server storage system
ImageContentType string `validate:"required" bun:",nullzero,notnull"` // MIME content type of the emoji image
ImageStaticContentType string `validate:"required" bun:",nullzero,notnull"` // MIME content type of the static version of the emoji image.
ImageFileSize int `validate:"required,min=1" bun:",nullzero,notnull"` // Size of the emoji image file in bytes, for serving purposes.
ImageStaticFileSize int `validate:"required,min=1" bun:",nullzero,notnull"` // Size of the static version of the emoji image file in bytes, for serving purposes.
ImageUpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the emoji image last updated?
Disabled *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Has a moderation action disabled this emoji from being shown?
URI string `validate:"url" bun:",nullzero,notnull,unique"` // ActivityPub uri of this emoji. Something like 'https://example.org/emojis/1234'
VisibleInPicker *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Is this emoji visible in the admin emoji picker?
Category *EmojiCategory `validate:"-" bun:"rel:belongs-to"` // In which emoji category is this emoji visible?
CategoryID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // ID of the category this emoji belongs to.
Cached *bool `validate:"-" bun:",nullzero,notnull,default:false"`
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Shortcode string `bun:",nullzero,notnull,unique:domainshortcode"` // String shortcode for this emoji -- the part that's between colons. This should be a-zA-Z_ eg., 'blob_hug' 'purple_heart' 'Gay_Otter' Must be unique with domain.
Domain string `bun:",nullzero,unique:domainshortcode"` // Origin domain of this emoji, eg 'example.org', 'queer.party'. empty string for local emojis.
ImageRemoteURL string `bun:",nullzero"` // Where can this emoji be retrieved remotely? Null for local emojis.
ImageStaticRemoteURL string `bun:",nullzero"` // Where can a static / non-animated version of this emoji be retrieved remotely? Null for local emojis.
ImageURL string `bun:",nullzero"` // Where can this emoji be retrieved from the local server? Null for remote emojis.
ImageStaticURL string `bun:",nullzero"` // Where can a static version of this emoji be retrieved from the local server? Null for remote emojis.
ImagePath string `bun:",nullzero,notnull"` // Path of the emoji image in the server storage system.
ImageStaticPath string `bun:",nullzero,notnull"` // Path of a static version of the emoji image in the server storage system
ImageContentType string `bun:",nullzero,notnull"` // MIME content type of the emoji image
ImageStaticContentType string `bun:",nullzero,notnull"` // MIME content type of the static version of the emoji image.
ImageFileSize int `bun:",nullzero,notnull"` // Size of the emoji image file in bytes, for serving purposes.
ImageStaticFileSize int `bun:",nullzero,notnull"` // Size of the static version of the emoji image file in bytes, for serving purposes.
ImageUpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the emoji image last updated?
Disabled *bool `bun:",nullzero,notnull,default:false"` // Has a moderation action disabled this emoji from being shown?
URI string `bun:",nullzero,notnull,unique"` // ActivityPub uri of this emoji. Something like 'https://example.org/emojis/1234'
VisibleInPicker *bool `bun:",nullzero,notnull,default:true"` // Is this emoji visible in the admin emoji picker?
Category *EmojiCategory `bun:"rel:belongs-to"` // In which emoji category is this emoji visible?
CategoryID string `bun:"type:CHAR(26),nullzero"` // ID of the category this emoji belongs to.
Cached *bool `bun:",nullzero,notnull,default:false"`
}

View file

@ -21,8 +21,8 @@ import "time"
// EmojiCategory represents a grouping of custom emojis.
type EmojiCategory struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Name string `validate:"required" bun:",nullzero,notnull,unique"` // name of this category
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Name string `bun:",nullzero,notnull,unique"` // name of this category
}

View file

@ -21,14 +21,14 @@ import "time"
// Follow represents one account following another, and the metadata around that follow.
type Follow struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
URI string `validate:"required,url" bun:",notnull,nullzero,unique"` // ActivityPub uri of this follow.
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who does this follow originate from?
Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who is the target of this follow ?
TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID
ShowReblogs *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts?
Notify *bool `validate:"-" bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts?
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
URI string `bun:",notnull,nullzero,unique"` // ActivityPub uri of this follow.
AccountID string `bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who does this follow originate from?
Account *Account `bun:"rel:belongs-to"` // Account corresponding to accountID
TargetAccountID string `bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who is the target of this follow ?
TargetAccount *Account `bun:"rel:belongs-to"` // Account corresponding to targetAccountID
ShowReblogs *bool `bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts?
Notify *bool `bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts?
}

View file

@ -21,14 +21,14 @@ import "time"
// FollowRequest represents one account requesting to follow another, and the metadata around that request.
type FollowRequest struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
URI string `validate:"required,url" bun:",notnull,nullzero,unique"` // ActivityPub uri of this follow (request).
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who does this follow request originate from?
Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who is the target of this follow request?
TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID
ShowReblogs *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts?
Notify *bool `validate:"-" bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts?
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
URI string `bun:",notnull,nullzero,unique"` // ActivityPub uri of this follow (request).
AccountID string `bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who does this follow request originate from?
Account *Account `bun:"rel:belongs-to"` // Account corresponding to accountID
TargetAccountID string `bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who is the target of this follow request?
TargetAccount *Account `bun:"rel:belongs-to"` // Account corresponding to targetAccountID
ShowReblogs *bool `bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts?
Notify *bool `bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts?
}

View file

@ -21,22 +21,22 @@ import "time"
// Instance represents a federated instance, either local or remote.
type Instance struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Domain string `validate:"required,fqdn" bun:",nullzero,notnull,unique"` // Instance domain eg example.org
Title string `validate:"-" bun:""` // Title of this instance as it would like to be displayed.
URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // base URI of this instance eg https://example.org
SuspendedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this instance suspended, if at all?
DomainBlockID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // ID of any existing domain block for this instance in the database
DomainBlock *DomainBlock `validate:"-" bun:"rel:belongs-to"` // Domain block corresponding to domainBlockID
ShortDescription string `validate:"-" bun:""` // Short description of this instance
Description string `validate:"-" bun:""` // Longer description of this instance
Terms string `validate:"-" bun:""` // Terms and conditions of this instance
ContactEmail string `validate:"omitempty,email" bun:""` // Contact email address for this instance
ContactAccountUsername string `validate:"required_with=ContactAccountID" bun:",nullzero"` // Username of the contact account for this instance
ContactAccountID string `validate:"required_with=ContactAccountUsername,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Contact account ID in the database for this instance
ContactAccount *Account `validate:"-" bun:"rel:belongs-to"` // account corresponding to contactAccountID
Reputation int64 `validate:"-" bun:",notnull,default:0"` // Reputation score of this instance
Version string `validate:"-" bun:",nullzero"` // Version of the software used on this instance
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Domain string `bun:",nullzero,notnull,unique"` // Instance domain eg example.org
Title string `bun:""` // Title of this instance as it would like to be displayed.
URI string `bun:",nullzero,notnull,unique"` // base URI of this instance eg https://example.org
SuspendedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this instance suspended, if at all?
DomainBlockID string `bun:"type:CHAR(26),nullzero"` // ID of any existing domain block for this instance in the database
DomainBlock *DomainBlock `bun:"rel:belongs-to"` // Domain block corresponding to domainBlockID
ShortDescription string `bun:""` // Short description of this instance
Description string `bun:""` // Longer description of this instance
Terms string `bun:""` // Terms and conditions of this instance
ContactEmail string `bun:""` // Contact email address for this instance
ContactAccountUsername string `bun:",nullzero"` // Username of the contact account for this instance
ContactAccountID string `bun:"type:CHAR(26),nullzero"` // Contact account ID in the database for this instance
ContactAccount *Account `bun:"rel:belongs-to"` // account corresponding to contactAccountID
Reputation int64 `bun:",notnull,default:0"` // Reputation score of this instance
Version string `bun:",nullzero"` // Version of the software used on this instance
}

View file

@ -21,24 +21,24 @@ import "time"
// List refers to a list of follows for which the owning account wants to view a timeline of posts.
type List struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Title string `validate:"required" bun:",nullzero,notnull,unique:listaccounttitle"` // Title of this list.
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero,unique:listaccounttitle"` // Account that created/owns the list
Account *Account `validate:"-" bun:"-"` // Account corresponding to accountID
ListEntries []*ListEntry `validate:"-" bun:"-"` // Entries contained by this list.
RepliesPolicy RepliesPolicy `validate:"-" bun:",nullzero,notnull,default:'followed'"` // RepliesPolicy for this list.
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Title string `bun:",nullzero,notnull,unique:listaccounttitle"` // Title of this list.
AccountID string `bun:"type:CHAR(26),notnull,nullzero,unique:listaccounttitle"` // Account that created/owns the list
Account *Account `bun:"-"` // Account corresponding to accountID
ListEntries []*ListEntry `bun:"-"` // Entries contained by this list.
RepliesPolicy RepliesPolicy `bun:",nullzero,notnull,default:'followed'"` // RepliesPolicy for this list.
}
// ListEntry refers to a single follow entry in a list.
type ListEntry struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
ListID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero,unique:listentrylistfollow"` // ID of the list that this entry belongs to.
FollowID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero,unique:listentrylistfollow"` // Follow that the account owning this entry wants to see posts of in the timeline.
Follow *Follow `validate:"-" bun:"-"` // Follow corresponding to followID.
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
ListID string `bun:"type:CHAR(26),notnull,nullzero,unique:listentrylistfollow"` // ID of the list that this entry belongs to.
FollowID string `bun:"type:CHAR(26),notnull,nullzero,unique:listentrylistfollow"` // Follow that the account owning this entry wants to see posts of in the timeline.
Follow *Follow `bun:"-"` // Follow corresponding to followID.
}
// RepliesPolicy denotes which replies should be shown in the list.

View file

@ -21,11 +21,11 @@ import "time"
// Marker stores a local account's read position on a given timeline.
type Marker struct {
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),pk,unique:markers_account_id_timeline_uniq,notnull,nullzero"` // ID of the local account that owns the marker
Name MarkerName `validate:"oneof=home notifications" bun:",nullzero,notnull,pk,unique:markers_account_id_timeline_uniq"` // Name of the marked timeline
UpdatedAt time.Time `validate:"required" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When marker was last updated
Version int `validate:"required,min=0" bun:",nullzero,notnull,default:0"` // For optimistic concurrency control
LastReadID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero"` // Last ID read on this timeline (status ID for home, notification ID for notifications)
AccountID string `bun:"type:CHAR(26),pk,unique:markers_account_id_timeline_uniq,notnull,nullzero"` // ID of the local account that owns the marker
Name MarkerName `bun:",nullzero,notnull,pk,unique:markers_account_id_timeline_uniq"` // Name of the marked timeline
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When marker was last updated
Version int `bun:",nullzero,notnull,default:0"` // For optimistic concurrency control
LastReadID string `bun:"type:CHAR(26),notnull,nullzero"` // Last ID read on this timeline (status ID for home, notification ID for notifications)
}
// MarkerName is the name of one of the timelines we can store markers for.

View file

@ -24,42 +24,42 @@ import (
// MediaAttachment represents a user-uploaded media attachment: an image/video/audio/gif that is
// somewhere in storage and that can be retrieved and served by the router.
type MediaAttachment struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
StatusID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // ID of the status to which this is attached
URL string `validate:"required_without=RemoteURL,omitempty,url" bun:",nullzero"` // Where can the attachment be retrieved on *this* server
RemoteURL string `validate:"required_without=URL,omitempty,url" bun:",nullzero"` // Where can the attachment be retrieved on a remote server (empty for local media)
Type FileType `validate:"oneof=Image Gifv Audio Video Unknown" bun:",nullzero,notnull"` // Type of file (image/gifv/audio/video)
FileMeta FileMeta `validate:"required" bun:",embed:,nullzero,notnull"` // Metadata about the file
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // To which account does this attachment belong
Description string `validate:"-" bun:""` // Description of the attachment (for screenreaders)
ScheduledStatusID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // To which scheduled status does this attachment belong
Blurhash string `validate:"required_if=Type Image,required_if=Type Gif,required_if=Type Video" bun:",nullzero"` // What is the generated blurhash of this attachment
Processing ProcessingStatus `validate:"oneof=0 1 2 666" bun:",notnull,default:2"` // What is the processing status of this attachment
File File `validate:"required" bun:",embed:file_,notnull,nullzero"` // metadata for the whole file
Thumbnail Thumbnail `validate:"required" bun:",embed:thumbnail_,notnull,nullzero"` // small image thumbnail derived from a larger image, video, or audio file.
Avatar *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment being used as an avatar?
Header *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment being used as a header?
Cached *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment currently cached by our instance?
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
StatusID string `bun:"type:CHAR(26),nullzero"` // ID of the status to which this is attached
URL string `bun:",nullzero"` // Where can the attachment be retrieved on *this* server
RemoteURL string `bun:",nullzero"` // Where can the attachment be retrieved on a remote server (empty for local media)
Type FileType `bun:",nullzero,notnull"` // Type of file (image/gifv/audio/video)
FileMeta FileMeta `bun:",embed:,nullzero,notnull"` // Metadata about the file
AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // To which account does this attachment belong
Description string `bun:""` // Description of the attachment (for screenreaders)
ScheduledStatusID string `bun:"type:CHAR(26),nullzero"` // To which scheduled status does this attachment belong
Blurhash string `bun:",nullzero"` // What is the generated blurhash of this attachment
Processing ProcessingStatus `bun:",notnull,default:2"` // What is the processing status of this attachment
File File `bun:",embed:file_,notnull,nullzero"` // metadata for the whole file
Thumbnail Thumbnail `bun:",embed:thumbnail_,notnull,nullzero"` // small image thumbnail derived from a larger image, video, or audio file.
Avatar *bool `bun:",nullzero,notnull,default:false"` // Is this attachment being used as an avatar?
Header *bool `bun:",nullzero,notnull,default:false"` // Is this attachment being used as a header?
Cached *bool `bun:",nullzero,notnull,default:false"` // Is this attachment currently cached by our instance?
}
// File refers to the metadata for the whole file
type File struct {
Path string `validate:"required,file" bun:",nullzero,notnull"` // Path of the file in storage.
ContentType string `validate:"required" bun:",nullzero,notnull"` // MIME content type of the file.
FileSize int `validate:"required" bun:",notnull"` // File size in bytes
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the file last updated.
Path string `bun:",nullzero,notnull"` // Path of the file in storage.
ContentType string `bun:",nullzero,notnull"` // MIME content type of the file.
FileSize int `bun:",notnull"` // File size in bytes
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the file last updated.
}
// Thumbnail refers to a small image thumbnail derived from a larger image, video, or audio file.
type Thumbnail struct {
Path string `validate:"required,file" bun:",nullzero,notnull"` // Path of the file in storage.
ContentType string `validate:"required" bun:",nullzero,notnull"` // MIME content type of the file.
FileSize int `validate:"required" bun:",notnull"` // File size in bytes
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the file last updated.
URL string `validate:"required_without=RemoteURL,omitempty,url" bun:",nullzero"` // What is the URL of the thumbnail on the local server
RemoteURL string `validate:"required_without=URL,omitempty,url" bun:",nullzero"` // What is the remote URL of the thumbnail (empty for local media)
Path string `bun:",nullzero,notnull"` // Path of the file in storage.
ContentType string `bun:",nullzero,notnull"` // MIME content type of the file.
FileSize int `bun:",notnull"` // File size in bytes
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the file last updated.
URL string `bun:",nullzero"` // What is the URL of the thumbnail on the local server
RemoteURL string `bun:",nullzero"` // What is the remote URL of the thumbnail (empty for local media)
}
// ProcessingStatus refers to how far along in the processing stage the attachment is.
@ -87,33 +87,33 @@ const (
// FileMeta describes metadata about the actual contents of the file.
type FileMeta struct {
Original Original `validate:"required" bun:"embed:original_"`
Original Original `bun:"embed:original_"`
Small Small `bun:"embed:small_"`
Focus Focus `bun:"embed:focus_"`
}
// Small can be used for a thumbnail of any media type
type Small struct {
Width int `validate:"required_with=Height Size Aspect"` // width in pixels
Height int `validate:"required_with=Width Size Aspect"` // height in pixels
Size int `validate:"required_with=Width Height Aspect"` // size in pixels (width * height)
Aspect float32 `validate:"required_with=Width Height Size"` // aspect ratio (width / height)
Width int // width in pixels
Height int // height in pixels
Size int // size in pixels (width * height)
Aspect float32 // aspect ratio (width / height)
}
// Original can be used for original metadata for any media type
type Original struct {
Width int `validate:"required_with=Height Size Aspect"` // width in pixels
Height int `validate:"required_with=Width Size Aspect"` // height in pixels
Size int `validate:"required_with=Width Height Aspect"` // size in pixels (width * height)
Aspect float32 `validate:"required_with=Width Height Size"` // aspect ratio (width / height)
Duration *float32 `validate:"-"` // video-specific: duration of the video in seconds
Framerate *float32 `validate:"-"` // video-specific: fps
Bitrate *uint64 `validate:"-"` // video-specific: bitrate
Width int // width in pixels
Height int // height in pixels
Size int // size in pixels (width * height)
Aspect float32 // aspect ratio (width / height)
Duration *float32 // video-specific: duration of the video in seconds
Framerate *float32 // video-specific: fps
Bitrate *uint64 // video-specific: bitrate
}
// Focus describes the 'center' of the image for display purposes.
// X and Y should each be between -1 and 1
type Focus struct {
X float32 `validate:"omitempty,max=1,min=-1"`
Y float32 `validate:"omitempty,max=1,min=-1"`
X float32
Y float32
}

View file

@ -24,17 +24,17 @@ import (
// Mention refers to the 'tagging' or 'mention' of a user within a status.
type Mention struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
StatusID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the status this mention originates from
Status *Status `validate:"-" bun:"rel:belongs-to"` // status referred to by statusID
OriginAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the mention creator account
OriginAccountURI string `validate:"url" bun:",nullzero,notnull"` // ActivityPub URI of the originator/creator of the mention
OriginAccount *Account `validate:"-" bun:"rel:belongs-to"` // account referred to by originAccountID
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // Mention target/receiver account ID
TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // account referred to by targetAccountID
Silent *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Prevent this mention from generating a notification?
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the status this mention originates from
Status *Status `bun:"rel:belongs-to"` // status referred to by statusID
OriginAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the mention creator account
OriginAccountURI string `bun:",nullzero,notnull"` // ActivityPub URI of the originator/creator of the mention
OriginAccount *Account `bun:"rel:belongs-to"` // account referred to by originAccountID
TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Mention target/receiver account ID
TargetAccount *Account `bun:"rel:belongs-to"` // account referred to by targetAccountID
Silent *bool `bun:",nullzero,notnull,default:false"` // Prevent this mention from generating a notification?
/*
NON-DATABASE CONVENIENCE FIELDS
@ -48,15 +48,15 @@ type Mention struct {
// @whatever_username@example.org
//
// This will not be put in the database, it's just for convenience.
NameString string `validate:"-" bun:"-"`
NameString string `bun:"-"`
// TargetAccountURI is the AP ID (uri) of the user mentioned.
//
// This will not be put in the database, it's just for convenience.
TargetAccountURI string `validate:"-" bun:"-"`
TargetAccountURI string `bun:"-"`
// TargetAccountURL is the web url of the user mentioned.
//
// This will not be put in the database, it's just for convenience.
TargetAccountURL string `validate:"-" bun:"-"`
TargetAccountURL string `bun:"-"`
// A pointer to the gtsmodel account of the mentioned account.
}

View file

@ -21,17 +21,17 @@ import "time"
// Notification models an alert/notification sent to an account about something like a reblog, like, new follow request, etc.
type Notification struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
NotificationType NotificationType `validate:"oneof=follow follow_request mention reblog favourite poll status" bun:",nullzero,notnull"` // Type of this notification
TargetAccountID string `validate:"ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the account targeted by the notification (ie., who will receive the notification?)
TargetAccount *Account `validate:"-" bun:"-"` // Account corresponding to TargetAccountID. Can be nil, always check first + select using ID if necessary.
OriginAccountID string `validate:"ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the account that performed the action that created the notification.
OriginAccount *Account `validate:"-" bun:"-"` // Account corresponding to OriginAccountID. Can be nil, always check first + select using ID if necessary.
StatusID string `validate:"required_if=NotificationType mention,required_if=NotificationType reblog,required_if=NotificationType favourite,required_if=NotificationType status,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // If the notification pertains to a status, what is the database ID of that status?
Status *Status `validate:"-" bun:"-"` // Status corresponding to StatusID. Can be nil, always check first + select using ID if necessary.
Read *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Notification has been seen/read
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
NotificationType NotificationType `bun:",nullzero,notnull"` // Type of this notification
TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the account targeted by the notification (ie., who will receive the notification?)
TargetAccount *Account `bun:"-"` // Account corresponding to TargetAccountID. Can be nil, always check first + select using ID if necessary.
OriginAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the account that performed the action that created the notification.
OriginAccount *Account `bun:"-"` // Account corresponding to OriginAccountID. Can be nil, always check first + select using ID if necessary.
StatusID string `bun:"type:CHAR(26),nullzero"` // If the notification pertains to a status, what is the database ID of that status?
Status *Status `bun:"-"` // Status corresponding to StatusID. Can be nil, always check first + select using ID if necessary.
Read *bool `bun:",nullzero,notnull,default:false"` // Notification has been seen/read
}
// NotificationType describes the reason/type of this notification.

View file

@ -26,20 +26,20 @@ import "time"
// or another instance, OR a report that was created remotely (on another instance)
// about a user on this instance, and received via the federated (s2s) API.
type Report struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
URI string `validate:"required,url" bun:",unique,nullzero,notnull"` // activitypub URI of this report
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // which account created this report
Account *Account `validate:"-" bun:"-"` // account corresponding to AccountID
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // which account is targeted by this report
TargetAccount *Account `validate:"-" bun:"-"` // account corresponding to TargetAccountID
Comment string `validate:"-" bun:",nullzero"` // comment / explanation for this report, by the reporter
StatusIDs []string `validate:"dive,ulid" bun:"statuses,array"` // database IDs of any statuses referenced by this report
Statuses []*Status `validate:"-" bun:"-"` // statuses corresponding to StatusIDs
Forwarded *bool `validate:"-" bun:",nullzero,notnull,default:false"` // flag to indicate report should be forwarded to remote instance
ActionTaken string `validate:"-" bun:",nullzero"` // string description of what action was taken in response to this report
ActionTakenAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // time at which action was taken, if any
ActionTakenByAccountID string `validate:",omitempty,ulid" bun:"type:CHAR(26),nullzero"` // database ID of account which took action, if any
ActionTakenByAccount *Account `validate:"-" bun:"-"` // account corresponding to ActionTakenByID, if any
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
URI string `bun:",unique,nullzero,notnull"` // activitypub URI of this report
AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // which account created this report
Account *Account `bun:"-"` // account corresponding to AccountID
TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // which account is targeted by this report
TargetAccount *Account `bun:"-"` // account corresponding to TargetAccountID
Comment string `bun:",nullzero"` // comment / explanation for this report, by the reporter
StatusIDs []string `bun:"statuses,array"` // database IDs of any statuses referenced by this report
Statuses []*Status `bun:"-"` // statuses corresponding to StatusIDs
Forwarded *bool `bun:",nullzero,notnull,default:false"` // flag to indicate report should be forwarded to remote instance
ActionTaken string `bun:",nullzero"` // string description of what action was taken in response to this report
ActionTakenAt time.Time `bun:"type:timestamptz,nullzero"` // time at which action was taken, if any
ActionTakenByAccountID string `bun:"type:CHAR(26),nullzero"` // database ID of account which took action, if any
ActionTakenByAccount *Account `bun:"-"` // account corresponding to ActionTakenByID, if any
}

View file

@ -21,9 +21,9 @@ import "time"
// RouterSession is used to store and retrieve settings for a router session.
type RouterSession struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Auth []byte `validate:"required,len=32" bun:"type:bytea,notnull,nullzero"`
Crypt []byte `validate:"required,len=32" bun:"type:bytea,notnull,nullzero"`
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Auth []byte `bun:"type:bytea,notnull,nullzero"`
Crypt []byte `bun:"type:bytea,notnull,nullzero"`
}

View file

@ -25,47 +25,47 @@ import (
// Status represents a user-created 'post' or 'status' in the database, either remote or local
type Status struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
FetchedAt time.Time `validate:"required_with=!Local" bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched.
PinnedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // Status was pinned by owning account at this time.
URI string `validate:"required,url" bun:",unique,nullzero,notnull"` // activitypub URI of this status
URL string `validate:"url" bun:",nullzero"` // web url for viewing this status
Content string `validate:"-" bun:""` // content of this status; likely html-formatted but not guaranteed
AttachmentIDs []string `validate:"dive,ulid" bun:"attachments,array"` // Database IDs of any media attachments associated with this status
Attachments []*MediaAttachment `validate:"-" bun:"attached_media,rel:has-many"` // Attachments corresponding to attachmentIDs
TagIDs []string `validate:"dive,ulid" bun:"tags,array"` // Database IDs of any tags used in this status
Tags []*Tag `validate:"-" bun:"attached_tags,m2m:status_to_tags"` // Tags corresponding to tagIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
MentionIDs []string `validate:"dive,ulid" bun:"mentions,array"` // Database IDs of any mentions in this status
Mentions []*Mention `validate:"-" bun:"attached_mentions,rel:has-many"` // Mentions corresponding to mentionIDs
EmojiIDs []string `validate:"dive,ulid" bun:"emojis,array"` // Database IDs of any emojis used in this status
Emojis []*Emoji `validate:"-" bun:"attached_emojis,m2m:status_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
Local *bool `validate:"-" bun:",nullzero,notnull,default:false"` // is this status from a local account?
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // which account posted this status?
Account *Account `validate:"-" bun:"rel:belongs-to"` // account corresponding to accountID
AccountURI string `validate:"required,url" bun:",nullzero,notnull"` // activitypub uri of the owner of this status
InReplyToID string `validate:"required_with=InReplyToURI InReplyToAccountID,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the status this status replies to
InReplyToURI string `validate:"required_with=InReplyToID InReplyToAccountID,omitempty,url" bun:",nullzero"` // activitypub uri of the status this status is a reply to
InReplyToAccountID string `validate:"required_with=InReplyToID InReplyToURI,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the account that this status replies to
InReplyTo *Status `validate:"-" bun:"-"` // status corresponding to inReplyToID
InReplyToAccount *Account `validate:"-" bun:"rel:belongs-to"` // account corresponding to inReplyToAccountID
BoostOfID string `validate:"required_with=BoostOfAccountID,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the status this status is a boost of
BoostOfAccountID string `validate:"required_with=BoostOfID,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the account that owns the boosted status
BoostOf *Status `validate:"-" bun:"-"` // status that corresponds to boostOfID
BoostOfAccount *Account `validate:"-" bun:"rel:belongs-to"` // account that corresponds to boostOfAccountID
ContentWarning string `validate:"-" bun:",nullzero"` // cw string for this status
Visibility Visibility `validate:"oneof=public unlocked followers_only mutuals_only direct" bun:",nullzero,notnull"` // visibility entry for this status
Sensitive *bool `validate:"-" bun:",nullzero,notnull,default:false"` // mark the status as sensitive?
Language string `validate:"-" bun:",nullzero"` // what language is this status written in?
CreatedWithApplicationID string `validate:"required_if=Local true,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Which application was used to create this status?
CreatedWithApplication *Application `validate:"-" bun:"rel:belongs-to"` // application corresponding to createdWithApplicationID
ActivityStreamsType string `validate:"required" bun:",nullzero,notnull"` // What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types. Will probably almost always be Note but who knows!.
Text string `validate:"-" bun:""` // Original text of the status without formatting
Federated *bool `validate:"-" bun:",notnull"` // This status will be federated beyond the local timeline(s)
Boostable *bool `validate:"-" bun:",notnull"` // This status can be boosted/reblogged
Replyable *bool `validate:"-" bun:",notnull"` // This status can be replied to
Likeable *bool `validate:"-" bun:",notnull"` // This status can be liked/faved
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
FetchedAt time.Time `bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched.
PinnedAt time.Time `bun:"type:timestamptz,nullzero"` // Status was pinned by owning account at this time.
URI string `bun:",unique,nullzero,notnull"` // activitypub URI of this status
URL string `bun:",nullzero"` // web url for viewing this status
Content string `bun:""` // content of this status; likely html-formatted but not guaranteed
AttachmentIDs []string `bun:"attachments,array"` // Database IDs of any media attachments associated with this status
Attachments []*MediaAttachment `bun:"attached_media,rel:has-many"` // Attachments corresponding to attachmentIDs
TagIDs []string `bun:"tags,array"` // Database IDs of any tags used in this status
Tags []*Tag `bun:"attached_tags,m2m:status_to_tags"` // Tags corresponding to tagIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
MentionIDs []string `bun:"mentions,array"` // Database IDs of any mentions in this status
Mentions []*Mention `bun:"attached_mentions,rel:has-many"` // Mentions corresponding to mentionIDs
EmojiIDs []string `bun:"emojis,array"` // Database IDs of any emojis used in this status
Emojis []*Emoji `bun:"attached_emojis,m2m:status_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
Local *bool `bun:",nullzero,notnull,default:false"` // is this status from a local account?
AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // which account posted this status?
Account *Account `bun:"rel:belongs-to"` // account corresponding to accountID
AccountURI string `bun:",nullzero,notnull"` // activitypub uri of the owner of this status
InReplyToID string `bun:"type:CHAR(26),nullzero"` // id of the status this status replies to
InReplyToURI string `bun:",nullzero"` // activitypub uri of the status this status is a reply to
InReplyToAccountID string `bun:"type:CHAR(26),nullzero"` // id of the account that this status replies to
InReplyTo *Status `bun:"-"` // status corresponding to inReplyToID
InReplyToAccount *Account `bun:"rel:belongs-to"` // account corresponding to inReplyToAccountID
BoostOfID string `bun:"type:CHAR(26),nullzero"` // id of the status this status is a boost of
BoostOfAccountID string `bun:"type:CHAR(26),nullzero"` // id of the account that owns the boosted status
BoostOf *Status `bun:"-"` // status that corresponds to boostOfID
BoostOfAccount *Account `bun:"rel:belongs-to"` // account that corresponds to boostOfAccountID
ContentWarning string `bun:",nullzero"` // cw string for this status
Visibility Visibility `bun:",nullzero,notnull"` // visibility entry for this status
Sensitive *bool `bun:",nullzero,notnull,default:false"` // mark the status as sensitive?
Language string `bun:",nullzero"` // what language is this status written in?
CreatedWithApplicationID string `bun:"type:CHAR(26),nullzero"` // Which application was used to create this status?
CreatedWithApplication *Application `bun:"rel:belongs-to"` // application corresponding to createdWithApplicationID
ActivityStreamsType string `bun:",nullzero,notnull"` // What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types. Will probably almost always be Note but who knows!.
Text string `bun:""` // Original text of the status without formatting
Federated *bool `bun:",notnull"` // This status will be federated beyond the local timeline(s)
Boostable *bool `bun:",notnull"` // This status can be boosted/reblogged
Replyable *bool `bun:",notnull"` // This status can be replied to
Likeable *bool `bun:",notnull"` // This status can be liked/faved
}
// GetID implements timeline.Timelineable{}.
@ -252,18 +252,18 @@ func (s *Status) MentionsAccount(id string) bool {
// StatusToTag is an intermediate struct to facilitate the many2many relationship between a status and one or more tags.
type StatusToTag struct {
StatusID string `validate:"ulid,required" bun:"type:CHAR(26),unique:statustag,nullzero,notnull"`
Status *Status `validate:"-" bun:"rel:belongs-to"`
TagID string `validate:"ulid,required" bun:"type:CHAR(26),unique:statustag,nullzero,notnull"`
Tag *Tag `validate:"-" bun:"rel:belongs-to"`
StatusID string `bun:"type:CHAR(26),unique:statustag,nullzero,notnull"`
Status *Status `bun:"rel:belongs-to"`
TagID string `bun:"type:CHAR(26),unique:statustag,nullzero,notnull"`
Tag *Tag `bun:"rel:belongs-to"`
}
// StatusToEmoji is an intermediate struct to facilitate the many2many relationship between a status and one or more emojis.
type StatusToEmoji struct {
StatusID string `validate:"ulid,required" bun:"type:CHAR(26),unique:statusemoji,nullzero,notnull"`
Status *Status `validate:"-" bun:"rel:belongs-to"`
EmojiID string `validate:"ulid,required" bun:"type:CHAR(26),unique:statusemoji,nullzero,notnull"`
Emoji *Emoji `validate:"-" bun:"rel:belongs-to"`
StatusID string `bun:"type:CHAR(26),unique:statusemoji,nullzero,notnull"`
Status *Status `bun:"rel:belongs-to"`
EmojiID string `bun:"type:CHAR(26),unique:statusemoji,nullzero,notnull"`
Emoji *Emoji `bun:"rel:belongs-to"`
}
// Visibility represents the visibility granularity of a status.

View file

@ -21,13 +21,13 @@ import "time"
// StatusBookmark refers to one account having a 'bookmark' of the status of another account.
type StatusBookmark struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id of the account that created ('did') the bookmark
Account *Account `validate:"-" bun:"rel:belongs-to"` // account that created the bookmark
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the bookmarked status
TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // account owning the bookmarked status
StatusID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // database id of the status that has been bookmarked
Status *Status `validate:"-" bun:"rel:belongs-to"` // the bookmarked status
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id of the account that created ('did') the bookmark
Account *Account `bun:"rel:belongs-to"` // account that created the bookmark
TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the bookmarked status
TargetAccount *Account `bun:"rel:belongs-to"` // account owning the bookmarked status
StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // database id of the status that has been bookmarked
Status *Status `bun:"rel:belongs-to"` // the bookmarked status
}

View file

@ -21,14 +21,14 @@ import "time"
// StatusFave refers to a 'fave' or 'like' in the database, from one account, targeting the status of another account
type StatusFave struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:statusfaveaccountstatus,nullzero,notnull"` // id of the account that created ('did') the fave
Account *Account `validate:"-" bun:"-"` // account that created the fave
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the faved status
TargetAccount *Account `validate:"-" bun:"-"` // account owning the faved status
StatusID string `validate:"required,ulid" bun:"type:CHAR(26),unique:statusfaveaccountstatus,nullzero,notnull"` // database id of the status that has been 'faved'
Status *Status `validate:"-" bun:"-"` // the faved status
URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // ActivityPub URI of this fave
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `bun:"type:CHAR(26),unique:statusfaveaccountstatus,nullzero,notnull"` // id of the account that created ('did') the fave
Account *Account `bun:"-"` // account that created the fave
TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the faved status
TargetAccount *Account `bun:"-"` // account owning the faved status
StatusID string `bun:"type:CHAR(26),unique:statusfaveaccountstatus,nullzero,notnull"` // database id of the status that has been 'faved'
Status *Status `bun:"-"` // the faved status
URI string `bun:",nullzero,notnull,unique"` // ActivityPub URI of this fave
}

View file

@ -21,13 +21,13 @@ import "time"
// StatusMute refers to one account having muted the status of another account or its own.
type StatusMute struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id of the account that created ('did') the mute
Account *Account `validate:"-" bun:"rel:belongs-to"` // pointer to the account specified by accountID
TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the muted status (can be the same as accountID)
TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // pointer to the account specified by targetAccountID
StatusID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // database id of the status that has been muted
Status *Status `validate:"-" bun:"rel:belongs-to"` // pointer to the muted status specified by statusID
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id of the account that created ('did') the mute
Account *Account `bun:"rel:belongs-to"` // pointer to the account specified by accountID
TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the muted status (can be the same as accountID)
TargetAccount *Account `bun:"rel:belongs-to"` // pointer to the account specified by targetAccountID
StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // database id of the status that has been muted
Status *Status `bun:"rel:belongs-to"` // pointer to the muted status specified by statusID
}

View file

@ -21,10 +21,10 @@ import "time"
// Tag represents a hashtag for gathering public statuses together.
type Tag struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Name string `validate:"required" bun:",unique,nullzero,notnull"` // (lowercase) name of the tag without the hash prefix
Useable *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Tag is useable on this instance.
Listable *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Tagged statuses can be listed on this instance.
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Name string `bun:",unique,nullzero,notnull"` // (lowercase) name of the tag without the hash prefix
Useable *bool `bun:",nullzero,notnull,default:true"` // Tag is useable on this instance.
Listable *bool `bun:",nullzero,notnull,default:true"` // Tagged statuses can be listed on this instance.
}

View file

@ -21,22 +21,22 @@ import "time"
// Token is a translation of the gotosocial token with the ExpiresIn fields replaced with ExpiresAt.
type Token struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
ClientID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the client who owns this token
UserID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero"` // ID of the user who owns this token
RedirectURI string `validate:"required,uri" bun:",nullzero,notnull"` // Oauth redirect URI for this token
Scope string `validate:"required" bun:",notnull"` // Oauth scope
Code string `validate:"-" bun:",pk,nullzero,notnull,default:''"` // Code, if present
CodeChallenge string `validate:"-" bun:",nullzero"` // Code challenge, if code present
CodeChallengeMethod string `validate:"-" bun:",nullzero"` // Code challenge method, if code present
CodeCreateAt time.Time `validate:"required_with=Code" bun:"type:timestamptz,nullzero"` // Code created time, if code present
CodeExpiresAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // Code expires at -- null means the code never expires
Access string `validate:"-" bun:",pk,nullzero,notnull,default:''"` // User level access token, if present
AccessCreateAt time.Time `validate:"required_with=Access" bun:"type:timestamptz,nullzero"` // User level access token created time, if access present
AccessExpiresAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // User level access token expires at -- null means the token never expires
Refresh string `validate:"-" bun:",pk,nullzero,notnull,default:''"` // Refresh token, if present
RefreshCreateAt time.Time `validate:"required_with=Refresh" bun:"type:timestamptz,nullzero"` // Refresh created at, if refresh present
RefreshExpiresAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // Refresh expires at -- null means the refresh token never expires
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
ClientID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the client who owns this token
UserID string `bun:"type:CHAR(26),nullzero"` // ID of the user who owns this token
RedirectURI string `bun:",nullzero,notnull"` // Oauth redirect URI for this token
Scope string `bun:",notnull"` // Oauth scope
Code string `bun:",pk,nullzero,notnull,default:''"` // Code, if present
CodeChallenge string `bun:",nullzero"` // Code challenge, if code present
CodeChallengeMethod string `bun:",nullzero"` // Code challenge method, if code present
CodeCreateAt time.Time `bun:"type:timestamptz,nullzero"` // Code created time, if code present
CodeExpiresAt time.Time `bun:"type:timestamptz,nullzero"` // Code expires at -- null means the code never expires
Access string `bun:",pk,nullzero,notnull,default:''"` // User level access token, if present
AccessCreateAt time.Time `bun:"type:timestamptz,nullzero"` // User level access token created time, if access present
AccessExpiresAt time.Time `bun:"type:timestamptz,nullzero"` // User level access token expires at -- null means the token never expires
Refresh string `bun:",pk,nullzero,notnull,default:''"` // Refresh token, if present
RefreshCreateAt time.Time `bun:"type:timestamptz,nullzero"` // Refresh created at, if refresh present
RefreshExpiresAt time.Time `bun:"type:timestamptz,nullzero"` // Refresh expires at -- null means the refresh token never expires
}

View file

@ -29,9 +29,9 @@ import (
// It's useful in cases where a remote account has been deleted, and we don't want to keep trying to process
// subsequent activities from that account, or deletes which target it.
type Tombstone struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Domain string `validate:"omitempty,fqdn" bun:",nullzero,notnull"` // Domain of the Object/Actor.
URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // ActivityPub URI for this Object/Actor.
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Domain string `bun:",nullzero,notnull"` // Domain of the Object/Actor.
URI string `bun:",nullzero,notnull,unique"` // ActivityPub URI for this Object/Actor.
}

View file

@ -25,35 +25,35 @@ import (
// User represents an actual human user of gotosocial. Note, this is a LOCAL gotosocial user, not a remote account.
// To cross reference this local user with their account (which can be local or remote), use the AccountID field.
type User struct {
ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Email string `validate:"required_with=ConfirmedAt" bun:",nullzero,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
AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull,unique"` // The id of the local gtsmodel.Account entry for this user.
Account *Account `validate:"-" bun:"rel:belongs-to"` // Pointer to the account of this user that corresponds to AccountID.
EncryptedPassword string `validate:"required" bun:",nullzero,notnull"` // 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.
SignUpIP net.IP `validate:"-" bun:",nullzero"` // From what IP was this user created?
CurrentSignInAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When did the user sign in with their current session.
CurrentSignInIP net.IP `validate:"-" bun:",nullzero"` // What's the most recent IP of this user
LastSignInAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When did this user last sign in?
LastSignInIP net.IP `validate:"-" bun:",nullzero"` // What's the previous IP of this user?
SignInCount int `validate:"min=0" bun:",notnull,default:0"` // How many times has this user signed in?
InviteID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the user who invited this user (who let this joker in?)
ChosenLanguages []string `validate:"-" bun:",nullzero"` // What languages does this user want to see?
FilteredLanguages []string `validate:"-" bun:",nullzero"` // What languages does this user not want to see?
Locale string `validate:"-" bun:",nullzero"` // In what timezone/locale is this user located?
CreatedByApplicationID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Which application id created this user? See gtsmodel.Application
CreatedByApplication *Application `validate:"-" bun:"rel:belongs-to"` // Pointer to the application corresponding to createdbyapplicationID.
LastEmailedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this user last contacted by email.
ConfirmationToken string `validate:"required_with=ConfirmationSentAt" bun:",nullzero"` // What confirmation token did we send this user/what are we expecting back?
ConfirmationSentAt time.Time `validate:"required_with=ConfirmationToken" bun:"type:timestamptz,nullzero"` // When did we send email confirmation to this user?
ConfirmedAt time.Time `validate:"required_with=Email" bun:"type:timestamptz,nullzero"` // When did the user confirm their email address
UnconfirmedEmail string `validate:"required_without=Email" bun:",nullzero"` // Email address that hasn't yet been confirmed
Moderator *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user a moderator?
Admin *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user an admin?
Disabled *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user disabled from posting?
Approved *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Has this user been approved by a moderator?
ResetPasswordToken string `validate:"required_with=ResetPasswordSentAt" bun:",nullzero"` // The generated token that the user can use to reset their password
ResetPasswordSentAt time.Time `validate:"required_with=ResetPasswordToken" bun:"type:timestamptz,nullzero"` // When did we email the user their reset-password email?
ExternalID string `validate:"-" bun:",nullzero,unique"` // If the login for the user is managed externally (e.g OIDC), we need to keep a stable reference to the external object (e.g OIDC sub claim)
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Email string `bun:",nullzero,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
AccountID string `bun:"type:CHAR(26),nullzero,notnull,unique"` // The id of the local gtsmodel.Account entry for this user.
Account *Account `bun:"rel:belongs-to"` // Pointer to the account of this user that corresponds to AccountID.
EncryptedPassword string `bun:",nullzero,notnull"` // 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.
SignUpIP net.IP `bun:",nullzero"` // From what IP was this user created?
CurrentSignInAt time.Time `bun:"type:timestamptz,nullzero"` // When did the user sign in with their current session.
CurrentSignInIP net.IP `bun:",nullzero"` // What's the most recent IP of this user
LastSignInAt time.Time `bun:"type:timestamptz,nullzero"` // When did this user last sign in?
LastSignInIP net.IP `bun:",nullzero"` // What's the previous IP of this user?
SignInCount int `bun:",notnull,default:0"` // How many times has this user signed in?
InviteID string `bun:"type:CHAR(26),nullzero"` // id of the user who invited this user (who let this joker in?)
ChosenLanguages []string `bun:",nullzero"` // What languages does this user want to see?
FilteredLanguages []string `bun:",nullzero"` // What languages does this user not want to see?
Locale string `bun:",nullzero"` // In what timezone/locale is this user located?
CreatedByApplicationID string `bun:"type:CHAR(26),nullzero"` // Which application id created this user? See gtsmodel.Application
CreatedByApplication *Application `bun:"rel:belongs-to"` // Pointer to the application corresponding to createdbyapplicationID.
LastEmailedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this user last contacted by email.
ConfirmationToken string `bun:",nullzero"` // What confirmation token did we send this user/what are we expecting back?
ConfirmationSentAt time.Time `bun:"type:timestamptz,nullzero"` // When did we send email confirmation to this user?
ConfirmedAt time.Time `bun:"type:timestamptz,nullzero"` // When did the user confirm their email address
UnconfirmedEmail string `bun:",nullzero"` // Email address that hasn't yet been confirmed
Moderator *bool `bun:",nullzero,notnull,default:false"` // Is this user a moderator?
Admin *bool `bun:",nullzero,notnull,default:false"` // Is this user an admin?
Disabled *bool `bun:",nullzero,notnull,default:false"` // Is this user disabled from posting?
Approved *bool `bun:",nullzero,notnull,default:false"` // Has this user been approved by a moderator?
ResetPasswordToken string `bun:",nullzero"` // The generated token that the user can use to reset their password
ResetPasswordSentAt time.Time `bun:"type:timestamptz,nullzero"` // When did we email the user their reset-password email?
ExternalID string `bun:",nullzero,unique"` // If the login for the user is managed externally (e.g OIDC), we need to keep a stable reference to the external object (e.g OIDC sub claim)
}

View file

@ -1,343 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"crypto/rand"
"crypto/rsa"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
"github.com/superseriousbusiness/gotosocial/testrig"
)
func happyAccount() *gtsmodel.Account {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
pub := &priv.PublicKey
return &gtsmodel.Account{
ID: "01F8MH1H7YV1Z7D2C8K2730QBF",
CreatedAt: time.Now().Add(-48 * time.Hour),
UpdatedAt: time.Now().Add(-48 * time.Hour),
Username: "the_mighty_zork",
Domain: "",
AvatarMediaAttachmentID: "01F8MH58A357CV5K7R7TJMSH6S",
AvatarMediaAttachment: nil,
AvatarRemoteURL: "",
HeaderMediaAttachmentID: "01PFPMWK2FF0D9WMHEJHR07C3Q",
HeaderMediaAttachment: nil,
HeaderRemoteURL: "",
DisplayName: "original zork (he/they)",
Fields: []*gtsmodel.Field{},
Note: "hey yo this is my profile!",
Memorial: testrig.FalseBool(),
AlsoKnownAs: "",
MovedToAccountID: "",
Bot: testrig.FalseBool(),
Reason: "I wanna be on this damned webbed site so bad! Please! Wow",
Locked: testrig.FalseBool(),
Discoverable: testrig.TrueBool(),
Privacy: gtsmodel.VisibilityPublic,
Sensitive: testrig.FalseBool(),
Language: "en",
StatusContentType: "text/plain",
URI: "http://localhost:8080/users/the_mighty_zork",
URL: "http://localhost:8080/@the_mighty_zork",
FetchedAt: time.Time{},
InboxURI: "http://localhost:8080/users/the_mighty_zork/inbox",
OutboxURI: "http://localhost:8080/users/the_mighty_zork/outbox",
FollowersURI: "http://localhost:8080/users/the_mighty_zork/followers",
FollowingURI: "http://localhost:8080/users/the_mighty_zork/following",
FeaturedCollectionURI: "http://localhost:8080/users/the_mighty_zork/collections/featured",
ActorType: ap.ActorPerson,
PrivateKey: priv,
PublicKey: pub,
PublicKeyURI: "http://localhost:8080/users/the_mighty_zork#main-key",
SensitizedAt: time.Time{},
SilencedAt: time.Time{},
SuspendedAt: time.Time{},
HideCollections: testrig.FalseBool(),
SuspensionOrigin: "",
}
}
type AccountValidateTestSuite struct {
suite.Suite
}
func (suite *AccountValidateTestSuite) TestValidateAccountHappyPath() {
// no problem here
a := happyAccount()
err := validate.Struct(*a)
suite.NoError(err)
}
// ID must be set and be valid ULID
func (suite *AccountValidateTestSuite) TestValidateAccountBadID() {
a := happyAccount()
a.ID = ""
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.ID' Error:Field validation for 'ID' failed on the 'required' tag")
a.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
// CreatedAt can be set or not -- it will be set in the database anyway
func (suite *AccountValidateTestSuite) TestValidateAccountNoCreatedAt() {
a := happyAccount()
a.CreatedAt = time.Time{}
err := validate.Struct(*a)
suite.NoError(err)
}
// FetchedAt must be defined if remote account
func (suite *AccountValidateTestSuite) TestValidateAccountNoWebfingeredAt() {
a := happyAccount()
a.Domain = "example.org"
a.FetchedAt = time.Time{}
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.FetchedAt' Error:Field validation for 'FetchedAt' failed on the 'required_with' tag")
}
// Username must be set
func (suite *AccountValidateTestSuite) TestValidateAccountUsername() {
a := happyAccount()
a.Username = ""
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Username' Error:Field validation for 'Username' failed on the 'required' tag")
}
// Domain must be either empty (for local accounts) or proper fqdn (for remote accounts)
func (suite *AccountValidateTestSuite) TestValidateAccountDomain() {
a := happyAccount()
a.FetchedAt = time.Now()
a.Domain = ""
err := validate.Struct(*a)
suite.NoError(err)
a.Domain = "localhost:8080"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
a.Domain = "ahhhhh"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
a.Domain = "https://www.example.org"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
a.Domain = "example.org:8080"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
a.Domain = "example.org"
err = validate.Struct(*a)
suite.NoError(err)
}
// Attachment IDs must either be not set, or must be valid ULID
func (suite *AccountValidateTestSuite) TestValidateAttachmentIDs() {
a := happyAccount()
a.AvatarMediaAttachmentID = ""
a.HeaderMediaAttachmentID = ""
err := validate.Struct(*a)
suite.NoError(err)
a.AvatarMediaAttachmentID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
a.HeaderMediaAttachmentID = "aaaa"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.AvatarMediaAttachmentID' Error:Field validation for 'AvatarMediaAttachmentID' failed on the 'ulid' tag\nKey: 'Account.HeaderMediaAttachmentID' Error:Field validation for 'HeaderMediaAttachmentID' failed on the 'ulid' tag")
}
// Attachment remote URLs must either not be set, or be valid URLs
func (suite *AccountValidateTestSuite) TestValidateAttachmentRemoteURLs() {
a := happyAccount()
a.AvatarRemoteURL = ""
a.HeaderRemoteURL = ""
err := validate.Struct(*a)
suite.NoError(err)
a.AvatarRemoteURL = "-------------"
a.HeaderRemoteURL = "https://valid-url.com"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.AvatarRemoteURL' Error:Field validation for 'AvatarRemoteURL' failed on the 'url' tag")
a.AvatarRemoteURL = "https://valid-url.com"
a.HeaderRemoteURL = ""
err = validate.Struct(*a)
suite.NoError(err)
}
// Default privacy must be set if account is local
func (suite *AccountValidateTestSuite) TestValidatePrivacy() {
a := happyAccount()
a.FetchedAt = time.Now()
a.Privacy = ""
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Privacy' Error:Field validation for 'Privacy' failed on the 'required_without' tag")
a.Privacy = "not valid"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Privacy' Error:Field validation for 'Privacy' failed on the 'oneof' tag")
a.Privacy = gtsmodel.VisibilityFollowersOnly
err = validate.Struct(*a)
suite.NoError(err)
a.Privacy = ""
a.Domain = "example.org"
err = validate.Struct(*a)
suite.NoError(err)
a.Privacy = "invalid"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Privacy' Error:Field validation for 'Privacy' failed on the 'oneof' tag")
}
// If set, language must be a valid language
func (suite *AccountValidateTestSuite) TestValidateLanguage() {
a := happyAccount()
a.Language = ""
err := validate.Struct(*a)
suite.NoError(err)
a.Language = "not valid"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.Language' Error:Field validation for 'Language' failed on the 'bcp47_language_tag' tag")
a.Language = "en-uk"
err = validate.Struct(*a)
suite.NoError(err)
}
// Account URI must be set and must be valid
func (suite *AccountValidateTestSuite) TestValidateAccountURI() {
a := happyAccount()
a.URI = "invalid-uri"
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.URI' Error:Field validation for 'URI' failed on the 'url' tag")
a.URI = ""
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.URI' Error:Field validation for 'URI' failed on the 'required' tag")
}
// ActivityPub URIs must be set on account if it's local
func (suite *AccountValidateTestSuite) TestValidateAccountURIs() {
a := happyAccount()
a.FetchedAt = time.Now()
a.InboxURI = "invalid-uri"
a.OutboxURI = "invalid-uri"
a.FollowersURI = "invalid-uri"
a.FollowingURI = "invalid-uri"
a.FeaturedCollectionURI = "invalid-uri"
a.PublicKeyURI = "invalid-uri"
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.InboxURI' Error:Field validation for 'InboxURI' failed on the 'url' tag\nKey: 'Account.OutboxURI' Error:Field validation for 'OutboxURI' failed on the 'url' tag\nKey: 'Account.FollowingURI' Error:Field validation for 'FollowingURI' failed on the 'url' tag\nKey: 'Account.FollowersURI' Error:Field validation for 'FollowersURI' failed on the 'url' tag\nKey: 'Account.FeaturedCollectionURI' Error:Field validation for 'FeaturedCollectionURI' failed on the 'url' tag\nKey: 'Account.PublicKeyURI' Error:Field validation for 'PublicKeyURI' failed on the 'url' tag")
a.InboxURI = ""
a.OutboxURI = ""
a.FollowersURI = ""
a.FollowingURI = ""
a.FeaturedCollectionURI = ""
a.PublicKeyURI = ""
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.InboxURI' Error:Field validation for 'InboxURI' failed on the 'required_without' tag\nKey: 'Account.OutboxURI' Error:Field validation for 'OutboxURI' failed on the 'required_without' tag\nKey: 'Account.FollowingURI' Error:Field validation for 'FollowingURI' failed on the 'required_without' tag\nKey: 'Account.FollowersURI' Error:Field validation for 'FollowersURI' failed on the 'required_without' tag\nKey: 'Account.FeaturedCollectionURI' Error:Field validation for 'FeaturedCollectionURI' failed on the 'required_without' tag\nKey: 'Account.PublicKeyURI' Error:Field validation for 'PublicKeyURI' failed on the 'required' tag")
a.Domain = "example.org"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.PublicKeyURI' Error:Field validation for 'PublicKeyURI' failed on the 'required' tag")
a.InboxURI = "invalid-uri"
a.OutboxURI = "invalid-uri"
a.FollowersURI = "invalid-uri"
a.FollowingURI = "invalid-uri"
a.FeaturedCollectionURI = "invalid-uri"
a.PublicKeyURI = "invalid-uri"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.InboxURI' Error:Field validation for 'InboxURI' failed on the 'url' tag\nKey: 'Account.OutboxURI' Error:Field validation for 'OutboxURI' failed on the 'url' tag\nKey: 'Account.FollowingURI' Error:Field validation for 'FollowingURI' failed on the 'url' tag\nKey: 'Account.FollowersURI' Error:Field validation for 'FollowersURI' failed on the 'url' tag\nKey: 'Account.FeaturedCollectionURI' Error:Field validation for 'FeaturedCollectionURI' failed on the 'url' tag\nKey: 'Account.PublicKeyURI' Error:Field validation for 'PublicKeyURI' failed on the 'url' tag")
}
// Actor type must be set and valid
func (suite *AccountValidateTestSuite) TestValidateActorType() {
a := happyAccount()
a.ActorType = ""
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.ActorType' Error:Field validation for 'ActorType' failed on the 'oneof' tag")
a.ActorType = "not valid"
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.ActorType' Error:Field validation for 'ActorType' failed on the 'oneof' tag")
a.ActorType = ap.ActivityArrive
err = validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.ActorType' Error:Field validation for 'ActorType' failed on the 'oneof' tag")
a.ActorType = ap.ActorOrganization
err = validate.Struct(*a)
suite.NoError(err)
}
// Private key must be set on local accounts
func (suite *AccountValidateTestSuite) TestValidatePrivateKey() {
a := happyAccount()
a.FetchedAt = time.Now()
a.PrivateKey = nil
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.PrivateKey' Error:Field validation for 'PrivateKey' failed on the 'required_without' tag")
a.Domain = "example.org"
err = validate.Struct(*a)
suite.NoError(err)
}
// Public key must be set
func (suite *AccountValidateTestSuite) TestValidatePublicKey() {
a := happyAccount()
a.PublicKey = nil
err := validate.Struct(*a)
suite.EqualError(err, "Key: 'Account.PublicKey' Error:Field validation for 'PublicKey' failed on the 'required' tag")
}
func TestAccountValidateTestSuite(t *testing.T) {
suite.Run(t, new(AccountValidateTestSuite))
}

View file

@ -1,132 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyApplication() *gtsmodel.Application {
return &gtsmodel.Application{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Name: "Tusky",
Website: "https://tusky.app",
RedirectURI: "oauth2redirect://com.keylesspalace.tusky/",
ClientID: "01FEEDMF6C0QD589MRK7919Z0R",
ClientSecret: "bd740cf1-024a-4e4d-8c39-866538f52fe6",
Scopes: "read write follow",
}
}
type ApplicationValidateTestSuite struct {
suite.Suite
}
func (suite *ApplicationValidateTestSuite) TestValidateApplicationHappyPath() {
// no problem here
a := happyApplication()
err := validate.Struct(a)
suite.NoError(err)
}
func (suite *ApplicationValidateTestSuite) TestValidateApplicationBadID() {
a := happyApplication()
a.ID = ""
err := validate.Struct(a)
suite.EqualError(err, "Key: 'Application.ID' Error:Field validation for 'ID' failed on the 'required' tag")
a.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(a)
suite.EqualError(err, "Key: 'Application.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *ApplicationValidateTestSuite) TestValidateApplicationNoCreatedAt() {
a := happyApplication()
a.CreatedAt = time.Time{}
err := validate.Struct(a)
suite.NoError(err)
}
func (suite *ApplicationValidateTestSuite) TestValidateApplicationName() {
a := happyApplication()
a.Name = ""
err := validate.Struct(a)
suite.EqualError(err, "Key: 'Application.Name' Error:Field validation for 'Name' failed on the 'required' tag")
}
func (suite *ApplicationValidateTestSuite) TestValidateApplicationWebsite() {
a := happyApplication()
a.Website = "invalid-website"
err := validate.Struct(a)
suite.EqualError(err, "Key: 'Application.Website' Error:Field validation for 'Website' failed on the 'url' tag")
a.Website = ""
err = validate.Struct(a)
suite.NoError(err)
}
func (suite *ApplicationValidateTestSuite) TestValidateApplicationRedirectURI() {
a := happyApplication()
a.RedirectURI = "invalid-uri"
err := validate.Struct(a)
suite.EqualError(err, "Key: 'Application.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'uri' tag")
a.RedirectURI = ""
err = validate.Struct(a)
suite.EqualError(err, "Key: 'Application.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'required' tag")
a.RedirectURI = "urn:ietf:wg:oauth:2.0:oob"
err = validate.Struct(a)
suite.NoError(err)
}
func (suite *ApplicationValidateTestSuite) TestValidateApplicationClientSecret() {
a := happyApplication()
a.ClientSecret = "invalid-uuid"
err := validate.Struct(a)
suite.EqualError(err, "Key: 'Application.ClientSecret' Error:Field validation for 'ClientSecret' failed on the 'uuid' tag")
a.ClientSecret = ""
err = validate.Struct(a)
suite.EqualError(err, "Key: 'Application.ClientSecret' Error:Field validation for 'ClientSecret' failed on the 'required' tag")
}
func (suite *ApplicationValidateTestSuite) TestValidateApplicationScopes() {
a := happyApplication()
a.Scopes = ""
err := validate.Struct(a)
suite.EqualError(err, "Key: 'Application.Scopes' Error:Field validation for 'Scopes' failed on the 'required' tag")
}
func TestApplicationValidateTestSuite(t *testing.T) {
suite.Run(t, new(ApplicationValidateTestSuite))
}

View file

@ -1,115 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyBlock() *gtsmodel.Block {
return &gtsmodel.Block{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
URI: "https://example.org/accounts/someone/blocks/01FE91RJR88PSEEE30EV35QR8N",
AccountID: "01FEED79PRMVWPRMFHFQM8MJQN",
Account: nil,
TargetAccountID: "01FEEDMF6C0QD589MRK7919Z0R",
TargetAccount: nil,
}
}
type BlockValidateTestSuite struct {
suite.Suite
}
func (suite *BlockValidateTestSuite) TestValidateBlockHappyPath() {
// no problem here
b := happyBlock()
err := validate.Struct(b)
suite.NoError(err)
}
func (suite *BlockValidateTestSuite) TestValidateBlockBadID() {
b := happyBlock()
b.ID = ""
err := validate.Struct(b)
suite.EqualError(err, "Key: 'Block.ID' Error:Field validation for 'ID' failed on the 'required' tag")
b.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(b)
suite.EqualError(err, "Key: 'Block.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *BlockValidateTestSuite) TestValidateBlockNoCreatedAt() {
b := happyBlock()
b.CreatedAt = time.Time{}
err := validate.Struct(b)
suite.NoError(err)
}
func (suite *BlockValidateTestSuite) TestValidateBlockCreatedByAccountID() {
b := happyBlock()
b.AccountID = ""
err := validate.Struct(b)
suite.EqualError(err, "Key: 'Block.AccountID' Error:Field validation for 'AccountID' failed on the 'required' tag")
b.AccountID = "this-is-not-a-valid-ulid"
err = validate.Struct(b)
suite.EqualError(err, "Key: 'Block.AccountID' Error:Field validation for 'AccountID' failed on the 'ulid' tag")
}
func (suite *BlockValidateTestSuite) TestValidateBlockTargetAccountID() {
b := happyBlock()
b.TargetAccountID = "invalid-ulid"
err := validate.Struct(b)
suite.EqualError(err, "Key: 'Block.TargetAccountID' Error:Field validation for 'TargetAccountID' failed on the 'ulid' tag")
b.TargetAccountID = "01FEEDHX4G7EGHF5GD9E82Y51Q"
err = validate.Struct(b)
suite.NoError(err)
b.TargetAccountID = ""
err = validate.Struct(b)
suite.EqualError(err, "Key: 'Block.TargetAccountID' Error:Field validation for 'TargetAccountID' failed on the 'required' tag")
}
func (suite *BlockValidateTestSuite) TestValidateBlockURI() {
b := happyBlock()
b.URI = "invalid-uri"
err := validate.Struct(b)
suite.EqualError(err, "Key: 'Block.URI' Error:Field validation for 'URI' failed on the 'url' tag")
b.URI = ""
err = validate.Struct(b)
suite.EqualError(err, "Key: 'Block.URI' Error:Field validation for 'URI' failed on the 'required' tag")
}
func TestBlockValidateTestSuite(t *testing.T) {
suite.Run(t, new(BlockValidateTestSuite))
}

View file

@ -1,101 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyClient() *gtsmodel.Client {
return &gtsmodel.Client{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Secret: "bd740cf1-024a-4e4d-8c39-866538f52fe6",
Domain: "oauth2redirect://com.keylesspalace.tusky/",
UserID: "01FEEDMF6C0QD589MRK7919Z0R",
}
}
type ClientValidateTestSuite struct {
suite.Suite
}
func (suite *ClientValidateTestSuite) TestValidateClientHappyPath() {
// no problem here
c := happyClient()
err := validate.Struct(c)
suite.NoError(err)
}
func (suite *ClientValidateTestSuite) TestValidateClientBadID() {
c := happyClient()
c.ID = ""
err := validate.Struct(c)
suite.EqualError(err, "Key: 'Client.ID' Error:Field validation for 'ID' failed on the 'required' tag")
c.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(c)
suite.EqualError(err, "Key: 'Client.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *ClientValidateTestSuite) TestValidateClientNoCreatedAt() {
c := happyClient()
c.CreatedAt = time.Time{}
err := validate.Struct(c)
suite.NoError(err)
}
func (suite *ClientValidateTestSuite) TestValidateClientDomain() {
c := happyClient()
c.Domain = "invalid-uri"
err := validate.Struct(c)
suite.EqualError(err, "Key: 'Client.Domain' Error:Field validation for 'Domain' failed on the 'uri' tag")
c.Domain = ""
err = validate.Struct(c)
suite.EqualError(err, "Key: 'Client.Domain' Error:Field validation for 'Domain' failed on the 'required' tag")
c.Domain = "urn:ietf:wg:oauth:2.0:oob"
err = validate.Struct(c)
suite.NoError(err)
}
func (suite *ClientValidateTestSuite) TestValidateSecret() {
c := happyClient()
c.Secret = "invalid-uuid"
err := validate.Struct(c)
suite.EqualError(err, "Key: 'Client.Secret' Error:Field validation for 'Secret' failed on the 'uuid' tag")
c.Secret = ""
err = validate.Struct(c)
suite.EqualError(err, "Key: 'Client.Secret' Error:Field validation for 'Secret' failed on the 'required' tag")
}
func TestClientValidateTestSuite(t *testing.T) {
suite.Run(t, new(ClientValidateTestSuite))
}

View file

@ -1,122 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
"github.com/superseriousbusiness/gotosocial/testrig"
)
func happyDomainBlock() *gtsmodel.DomainBlock {
return &gtsmodel.DomainBlock{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Domain: "baddudes.suck",
CreatedByAccountID: "01FEED79PRMVWPRMFHFQM8MJQN",
PrivateComment: "we don't like em",
PublicComment: "poo poo dudes",
Obfuscate: testrig.FalseBool(),
SubscriptionID: "",
}
}
type DomainBlockValidateTestSuite struct {
suite.Suite
}
func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockHappyPath() {
// no problem here
d := happyDomainBlock()
err := validate.Struct(d)
suite.NoError(err)
}
func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockBadID() {
d := happyDomainBlock()
d.ID = ""
err := validate.Struct(d)
suite.EqualError(err, "Key: 'DomainBlock.ID' Error:Field validation for 'ID' failed on the 'required' tag")
d.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(d)
suite.EqualError(err, "Key: 'DomainBlock.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockNoCreatedAt() {
d := happyDomainBlock()
d.CreatedAt = time.Time{}
err := validate.Struct(d)
suite.NoError(err)
}
func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockBadDomain() {
d := happyDomainBlock()
d.Domain = ""
err := validate.Struct(d)
suite.EqualError(err, "Key: 'DomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'required' tag")
d.Domain = "this-is-not-a-valid-domain"
err = validate.Struct(d)
suite.EqualError(err, "Key: 'DomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
}
func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockCreatedByAccountID() {
d := happyDomainBlock()
d.CreatedByAccountID = ""
err := validate.Struct(d)
suite.EqualError(err, "Key: 'DomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'required' tag")
d.CreatedByAccountID = "this-is-not-a-valid-ulid"
err = validate.Struct(d)
suite.EqualError(err, "Key: 'DomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'ulid' tag")
}
func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockComments() {
d := happyDomainBlock()
d.PrivateComment = ""
d.PublicComment = ""
err := validate.Struct(d)
suite.NoError(err)
}
func (suite *DomainBlockValidateTestSuite) TestValidateDomainSubscriptionID() {
d := happyDomainBlock()
d.SubscriptionID = "invalid-ulid"
err := validate.Struct(d)
suite.EqualError(err, "Key: 'DomainBlock.SubscriptionID' Error:Field validation for 'SubscriptionID' failed on the 'ulid' tag")
d.SubscriptionID = "01FEEDHX4G7EGHF5GD9E82Y51Q"
err = validate.Struct(d)
suite.NoError(err)
}
func TestDomainBlockValidateTestSuite(t *testing.T) {
suite.Run(t, new(DomainBlockValidateTestSuite))
}

View file

@ -1,96 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyEmailDomainBlock() *gtsmodel.EmailDomainBlock {
return &gtsmodel.EmailDomainBlock{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Domain: "baddudes.suck",
CreatedByAccountID: "01FEED79PRMVWPRMFHFQM8MJQN",
}
}
type EmailDomainBlockValidateTestSuite struct {
suite.Suite
}
func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockHappyPath() {
// no problem here
e := happyEmailDomainBlock()
err := validate.Struct(e)
suite.NoError(err)
}
func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockBadID() {
e := happyEmailDomainBlock()
e.ID = ""
err := validate.Struct(e)
suite.EqualError(err, "Key: 'EmailDomainBlock.ID' Error:Field validation for 'ID' failed on the 'required' tag")
e.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(e)
suite.EqualError(err, "Key: 'EmailDomainBlock.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockNoCreatedAt() {
e := happyEmailDomainBlock()
e.CreatedAt = time.Time{}
err := validate.Struct(e)
suite.NoError(err)
}
func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockBadDomain() {
e := happyEmailDomainBlock()
e.Domain = ""
err := validate.Struct(e)
suite.EqualError(err, "Key: 'EmailDomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'required' tag")
e.Domain = "this-is-not-a-valid-domain"
err = validate.Struct(e)
suite.EqualError(err, "Key: 'EmailDomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
}
func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockCreatedByAccountID() {
e := happyEmailDomainBlock()
e.CreatedByAccountID = ""
err := validate.Struct(e)
suite.EqualError(err, "Key: 'EmailDomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'required' tag")
e.CreatedByAccountID = "this-is-not-a-valid-ulid"
err = validate.Struct(e)
suite.EqualError(err, "Key: 'EmailDomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'ulid' tag")
}
func TestEmailDomainBlockValidateTestSuite(t *testing.T) {
suite.Run(t, new(EmailDomainBlockValidateTestSuite))
}

View file

@ -1,195 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
"github.com/superseriousbusiness/gotosocial/testrig"
)
func happyEmoji() *gtsmodel.Emoji {
// the file validator actually runs os.Stat on given paths, so we need to just create small
// temp files for both the main attachment file and the thumbnail
imageFile, err := os.CreateTemp("", "gts_test_emoji")
if err != nil {
panic(err)
}
if _, err := imageFile.WriteString("main"); err != nil {
panic(err)
}
imagePath := imageFile.Name()
if err := imageFile.Close(); err != nil {
panic(err)
}
staticFile, err := os.CreateTemp("", "gts_test_emoji_static")
if err != nil {
panic(err)
}
if _, err := staticFile.WriteString("thumbnail"); err != nil {
panic(err)
}
imageStaticPath := staticFile.Name()
if err := staticFile.Close(); err != nil {
panic(err)
}
return &gtsmodel.Emoji{
ID: "01F8MH6NEM8D7527KZAECTCR76",
CreatedAt: time.Now().Add(-71 * time.Hour),
UpdatedAt: time.Now().Add(-71 * time.Hour),
Shortcode: "blob_test",
Domain: "example.org",
ImageRemoteURL: "https://example.org/emojis/blob_test.gif",
ImageStaticRemoteURL: "https://example.org/emojis/blob_test.png",
ImageURL: "",
ImageStaticURL: "",
ImagePath: imagePath,
ImageStaticPath: imageStaticPath,
ImageContentType: "image/gif",
ImageStaticContentType: "image/png",
ImageFileSize: 1024,
ImageStaticFileSize: 256,
ImageUpdatedAt: time.Now(),
Disabled: testrig.FalseBool(),
URI: "https://example.org/emojis/blob_test",
VisibleInPicker: testrig.TrueBool(),
CategoryID: "01FEE47ZH70PWDSEAVBRFNX325",
}
}
type EmojiValidateTestSuite struct {
suite.Suite
}
func (suite *EmojiValidateTestSuite) TestValidateEmojiHappyPath() {
// no problem here
m := happyEmoji()
err := validate.Struct(*m)
suite.NoError(err)
}
func (suite *EmojiValidateTestSuite) TestValidateEmojiBadFilePaths() {
e := happyEmoji()
e.ImagePath = "/tmp/nonexistent/file/for/gotosocial/test"
err := validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag")
e.ImagePath = ""
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'required' tag")
e.ImagePath = "???????????thisnot a valid path####"
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag")
e.ImageStaticPath = "/tmp/nonexistent/file/for/gotosocial/test"
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag\nKey: 'Emoji.ImageStaticPath' Error:Field validation for 'ImageStaticPath' failed on the 'file' tag")
e.ImageStaticPath = ""
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag\nKey: 'Emoji.ImageStaticPath' Error:Field validation for 'ImageStaticPath' failed on the 'required' tag")
e.ImageStaticPath = "???????????thisnot a valid path####"
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag\nKey: 'Emoji.ImageStaticPath' Error:Field validation for 'ImageStaticPath' failed on the 'file' tag")
}
func (suite *EmojiValidateTestSuite) TestValidateEmojiURI() {
e := happyEmoji()
e.URI = "aaaaaaaaaa"
err := validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.URI' Error:Field validation for 'URI' failed on the 'url' tag")
e.URI = ""
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.URI' Error:Field validation for 'URI' failed on the 'url' tag")
}
func (suite *EmojiValidateTestSuite) TestValidateEmojiURLCombos() {
e := happyEmoji()
e.ImageRemoteURL = ""
err := validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImageRemoteURL' Error:Field validation for 'ImageRemoteURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageURL' Error:Field validation for 'ImageURL' failed on the 'required_without' tag")
e.ImageURL = "https://whatever.org"
err = validate.Struct(e)
suite.NoError(err)
e.ImageStaticRemoteURL = ""
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImageStaticRemoteURL' Error:Field validation for 'ImageStaticRemoteURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageStaticURL' Error:Field validation for 'ImageStaticURL' failed on the 'required_without' tag")
e.ImageStaticURL = "https://whatever.org"
err = validate.Struct(e)
suite.NoError(err)
e.ImageURL = ""
e.ImageStaticURL = ""
e.ImageRemoteURL = ""
e.ImageStaticRemoteURL = ""
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImageRemoteURL' Error:Field validation for 'ImageRemoteURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageStaticRemoteURL' Error:Field validation for 'ImageStaticRemoteURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageURL' Error:Field validation for 'ImageURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageStaticURL' Error:Field validation for 'ImageStaticURL' failed on the 'required_without' tag")
}
func (suite *EmojiValidateTestSuite) TestValidateFileSize() {
e := happyEmoji()
e.ImageFileSize = 0
err := validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImageFileSize' Error:Field validation for 'ImageFileSize' failed on the 'required' tag")
e.ImageStaticFileSize = 0
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImageFileSize' Error:Field validation for 'ImageFileSize' failed on the 'required' tag\nKey: 'Emoji.ImageStaticFileSize' Error:Field validation for 'ImageStaticFileSize' failed on the 'required' tag")
e.ImageFileSize = -1
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImageFileSize' Error:Field validation for 'ImageFileSize' failed on the 'min' tag\nKey: 'Emoji.ImageStaticFileSize' Error:Field validation for 'ImageStaticFileSize' failed on the 'required' tag")
e.ImageStaticFileSize = -1
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImageFileSize' Error:Field validation for 'ImageFileSize' failed on the 'min' tag\nKey: 'Emoji.ImageStaticFileSize' Error:Field validation for 'ImageStaticFileSize' failed on the 'min' tag")
}
func (suite *EmojiValidateTestSuite) TestValidateDomain() {
e := happyEmoji()
e.Domain = ""
err := validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.ImageURL' Error:Field validation for 'ImageURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageStaticURL' Error:Field validation for 'ImageStaticURL' failed on the 'required_without' tag")
e.Domain = "aaaaaaaaa"
err = validate.Struct(e)
suite.EqualError(err, "Key: 'Emoji.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
}
func TestEmojiValidateTestSuite(t *testing.T) {
suite.Run(t, new(EmojiValidateTestSuite))
}

View file

@ -1,87 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyFollow() *gtsmodel.Follow {
return &gtsmodel.Follow{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
AccountID: "01FE96MAE58MXCE5C4SSMEMCEK",
Account: nil,
TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A",
TargetAccount: nil,
URI: "https://example.org/users/user1/activity/follow/01FE91RJR88PSEEE30EV35QR8N",
}
}
type FollowValidateTestSuite struct {
suite.Suite
}
func (suite *FollowValidateTestSuite) TestValidateFollowHappyPath() {
// no problem here
f := happyFollow()
err := validate.Struct(f)
suite.NoError(err)
}
func (suite *FollowValidateTestSuite) TestValidateFollowBadID() {
f := happyFollow()
f.ID = ""
err := validate.Struct(f)
suite.EqualError(err, "Key: 'Follow.ID' Error:Field validation for 'ID' failed on the 'required' tag")
f.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(f)
suite.EqualError(err, "Key: 'Follow.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *FollowValidateTestSuite) TestValidateFollowNoCreatedAt() {
f := happyFollow()
f.CreatedAt = time.Time{}
err := validate.Struct(f)
suite.NoError(err)
}
func (suite *FollowValidateTestSuite) TestValidateFollowNoURI() {
f := happyFollow()
f.URI = ""
err := validate.Struct(f)
suite.EqualError(err, "Key: 'Follow.URI' Error:Field validation for 'URI' failed on the 'required' tag")
f.URI = "this-is-not-a-valid-url"
err = validate.Struct(f)
suite.EqualError(err, "Key: 'Follow.URI' Error:Field validation for 'URI' failed on the 'url' tag")
}
func TestFollowValidateTestSuite(t *testing.T) {
suite.Run(t, new(FollowValidateTestSuite))
}

View file

@ -1,87 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyFollowRequest() *gtsmodel.FollowRequest {
return &gtsmodel.FollowRequest{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
AccountID: "01FE96MAE58MXCE5C4SSMEMCEK",
Account: nil,
TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A",
TargetAccount: nil,
URI: "https://example.org/users/user1/activity/follow/01FE91RJR88PSEEE30EV35QR8N",
}
}
type FollowRequestValidateTestSuite struct {
suite.Suite
}
func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestHappyPath() {
// no problem here
f := happyFollowRequest()
err := validate.Struct(f)
suite.NoError(err)
}
func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestBadID() {
f := happyFollowRequest()
f.ID = ""
err := validate.Struct(f)
suite.EqualError(err, "Key: 'FollowRequest.ID' Error:Field validation for 'ID' failed on the 'required' tag")
f.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(f)
suite.EqualError(err, "Key: 'FollowRequest.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestNoCreatedAt() {
f := happyFollowRequest()
f.CreatedAt = time.Time{}
err := validate.Struct(f)
suite.NoError(err)
}
func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestNoURI() {
f := happyFollowRequest()
f.URI = ""
err := validate.Struct(f)
suite.EqualError(err, "Key: 'FollowRequest.URI' Error:Field validation for 'URI' failed on the 'required' tag")
f.URI = "this-is-not-a-valid-url"
err = validate.Struct(f)
suite.EqualError(err, "Key: 'FollowRequest.URI' Error:Field validation for 'URI' failed on the 'url' tag")
}
func TestFollowRequestValidateTestSuite(t *testing.T) {
suite.Run(t, new(FollowRequestValidateTestSuite))
}

View file

@ -1,145 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyInstance() *gtsmodel.Instance {
return &gtsmodel.Instance{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Domain: "example.org",
Title: "Example Instance",
URI: "https://example.org",
SuspendedAt: time.Time{},
DomainBlockID: "",
DomainBlock: nil,
ShortDescription: "This is a description for the example/testing instance.",
Description: "This is a way longer description for the example/testing instance!",
Terms: "Don't be a knobhead.",
ContactEmail: "admin@example.org",
ContactAccountUsername: "admin",
ContactAccountID: "01FEE20H5QWHJDEXAEE9G96PR0",
ContactAccount: nil,
Reputation: 420,
Version: "gotosocial 0.1.0",
}
}
type InstanceValidateTestSuite struct {
suite.Suite
}
func (suite *InstanceValidateTestSuite) TestValidateInstanceHappyPath() {
// no problem here
m := happyInstance()
err := validate.Struct(*m)
suite.NoError(err)
}
func (suite *InstanceValidateTestSuite) TestValidateInstanceBadID() {
m := happyInstance()
m.ID = ""
err := validate.Struct(*m)
suite.EqualError(err, "Key: 'Instance.ID' Error:Field validation for 'ID' failed on the 'required' tag")
m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(*m)
suite.EqualError(err, "Key: 'Instance.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *InstanceValidateTestSuite) TestValidateInstanceAccountURI() {
i := happyInstance()
i.URI = ""
err := validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.URI' Error:Field validation for 'URI' failed on the 'required' tag")
i.URI = "---------------------------"
err = validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.URI' Error:Field validation for 'URI' failed on the 'url' tag")
}
func (suite *InstanceValidateTestSuite) TestValidateInstanceDodgyAccountID() {
i := happyInstance()
i.ContactAccountID = "9HZJ76B6VXSKF"
err := validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.ContactAccountID' Error:Field validation for 'ContactAccountID' failed on the 'ulid' tag")
i.ContactAccountID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!"
err = validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.ContactAccountID' Error:Field validation for 'ContactAccountID' failed on the 'ulid' tag")
i.ContactAccountID = ""
err = validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.ContactAccountID' Error:Field validation for 'ContactAccountID' failed on the 'required_with' tag")
i.ContactAccountUsername = ""
err = validate.Struct(i)
suite.NoError(err)
}
func (suite *InstanceValidateTestSuite) TestValidateInstanceDomain() {
i := happyInstance()
i.Domain = "poopoo"
err := validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
i.Domain = ""
err = validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.Domain' Error:Field validation for 'Domain' failed on the 'required' tag")
i.Domain = "https://aaaaaaaaaaaaah.org"
err = validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag")
}
func (suite *InstanceValidateTestSuite) TestValidateInstanceContactEmail() {
i := happyInstance()
i.ContactEmail = "poopoo"
err := validate.Struct(i)
suite.EqualError(err, "Key: 'Instance.ContactEmail' Error:Field validation for 'ContactEmail' failed on the 'email' tag")
i.ContactEmail = ""
err = validate.Struct(i)
suite.NoError(err)
}
func (suite *InstanceValidateTestSuite) TestValidateInstanceNoCreatedAt() {
i := happyInstance()
i.CreatedAt = time.Time{}
err := validate.Struct(i)
suite.NoError(err)
}
func TestInstanceValidateTestSuite(t *testing.T) {
suite.Run(t, new(InstanceValidateTestSuite))
}

View file

@ -1,230 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
"github.com/superseriousbusiness/gotosocial/testrig"
)
func happyMediaAttachment() *gtsmodel.MediaAttachment {
// the file validator actually runs os.Stat on given paths, so we need to just create small
// temp files for both the main attachment file and the thumbnail
mainFile, err := os.CreateTemp("", "gts_test_mainfile")
if err != nil {
panic(err)
}
if _, err := mainFile.WriteString("main"); err != nil {
panic(err)
}
mainPath := mainFile.Name()
if err := mainFile.Close(); err != nil {
panic(err)
}
thumbnailFile, err := os.CreateTemp("", "gts_test_thumbnail")
if err != nil {
panic(err)
}
if _, err := thumbnailFile.WriteString("thumbnail"); err != nil {
panic(err)
}
thumbnailPath := thumbnailFile.Name()
if err := thumbnailFile.Close(); err != nil {
panic(err)
}
return &gtsmodel.MediaAttachment{
ID: "01F8MH6NEM8D7527KZAECTCR76",
CreatedAt: time.Now().Add(-71 * time.Hour),
UpdatedAt: time.Now().Add(-71 * time.Hour),
StatusID: "01F8MH75CBF9JFX4ZAD54N0W0R",
URL: "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg",
RemoteURL: "",
Type: gtsmodel.FileTypeImage,
FileMeta: gtsmodel.FileMeta{
Original: gtsmodel.Original{
Width: 1200,
Height: 630,
Size: 756000,
Aspect: 1.9047619047619047,
},
Small: gtsmodel.Small{
Width: 256,
Height: 134,
Size: 34304,
Aspect: 1.9104477611940298,
},
},
AccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
Description: "Black and white image of some 50's style text saying: Welcome On Board",
ScheduledStatusID: "",
Blurhash: "LNJRdVM{00Rj%Mayt7j[4nWBofRj",
Processing: 2,
File: gtsmodel.File{
Path: mainPath,
ContentType: "image/jpeg",
FileSize: 62529,
UpdatedAt: time.Now().Add(-71 * time.Hour),
},
Thumbnail: gtsmodel.Thumbnail{
Path: thumbnailPath,
ContentType: "image/jpeg",
FileSize: 6872,
UpdatedAt: time.Now().Add(-71 * time.Hour),
URL: "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/small/01F8MH6NEM8D7527KZAECTCR76.jpg",
RemoteURL: "",
},
Avatar: testrig.FalseBool(),
Header: testrig.FalseBool(),
}
}
type MediaAttachmentValidateTestSuite struct {
suite.Suite
}
func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentHappyPath() {
// no problem here
m := happyMediaAttachment()
err := validate.Struct(m)
suite.NoError(err)
}
func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadFilePaths() {
m := happyMediaAttachment()
m.File.Path = "/tmp/nonexistent/file/for/gotosocial/test"
err := validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag")
m.File.Path = ""
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'required' tag")
m.File.Path = "???????????thisnot a valid path####"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag")
m.Thumbnail.Path = "/tmp/nonexistent/file/for/gotosocial/test"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag\nKey: 'MediaAttachment.Thumbnail.Path' Error:Field validation for 'Path' failed on the 'file' tag")
m.Thumbnail.Path = ""
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag\nKey: 'MediaAttachment.Thumbnail.Path' Error:Field validation for 'Path' failed on the 'required' tag")
m.Thumbnail.Path = "???????????thisnot a valid path####"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag\nKey: 'MediaAttachment.Thumbnail.Path' Error:Field validation for 'Path' failed on the 'file' tag")
}
func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadType() {
m := happyMediaAttachment()
m.Type = ""
err := validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.Type' Error:Field validation for 'Type' failed on the 'oneof' tag")
m.Type = "Not Supported"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.Type' Error:Field validation for 'Type' failed on the 'oneof' tag")
}
func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadFileMeta() {
m := happyMediaAttachment()
m.FileMeta.Original.Aspect = 0
err := validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.FileMeta.Original.Aspect' Error:Field validation for 'Aspect' failed on the 'required_with' tag")
m.FileMeta.Original.Height = 0
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.FileMeta.Original.Height' Error:Field validation for 'Height' failed on the 'required_with' tag\nKey: 'MediaAttachment.FileMeta.Original.Aspect' Error:Field validation for 'Aspect' failed on the 'required_with' tag")
m.FileMeta.Original = gtsmodel.Original{}
err = validate.Struct(m)
suite.NoError(err)
m.FileMeta.Focus.X = 3.6
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.FileMeta.Focus.X' Error:Field validation for 'X' failed on the 'max' tag")
m.FileMeta.Focus.Y = -50
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.FileMeta.Focus.X' Error:Field validation for 'X' failed on the 'max' tag\nKey: 'MediaAttachment.FileMeta.Focus.Y' Error:Field validation for 'Y' failed on the 'min' tag")
}
func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadURLCombos() {
m := happyMediaAttachment()
m.URL = "aaaaaaaaaa"
err := validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.URL' Error:Field validation for 'URL' failed on the 'url' tag")
m.URL = ""
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.URL' Error:Field validation for 'URL' failed on the 'required_without' tag\nKey: 'MediaAttachment.RemoteURL' Error:Field validation for 'RemoteURL' failed on the 'required_without' tag")
m.RemoteURL = "oooooooooo"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.RemoteURL' Error:Field validation for 'RemoteURL' failed on the 'url' tag")
m.RemoteURL = "https://a-valid-url.gay"
err = validate.Struct(m)
suite.NoError(err)
}
func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBlurhash() {
m := happyMediaAttachment()
m.Blurhash = ""
err := validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.Blurhash' Error:Field validation for 'Blurhash' failed on the 'required_if' tag")
m.Type = gtsmodel.FileTypeAudio
err = validate.Struct(m)
suite.NoError(err)
m.Blurhash = "some_blurhash"
err = validate.Struct(m)
suite.NoError(err)
}
func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentProcessing() {
m := happyMediaAttachment()
m.Processing = 420
err := validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.Processing' Error:Field validation for 'Processing' failed on the 'oneof' tag")
m.Processing = -5
err = validate.Struct(m)
suite.EqualError(err, "Key: 'MediaAttachment.Processing' Error:Field validation for 'Processing' failed on the 'oneof' tag")
}
func TestMediaAttachmentValidateTestSuite(t *testing.T) {
suite.Run(t, new(MediaAttachmentValidateTestSuite))
}

View file

@ -1,101 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyMention() *gtsmodel.Mention {
return &gtsmodel.Mention{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
OriginAccountID: "01FE96MAE58MXCE5C4SSMEMCEK",
OriginAccountURI: "https://some-instance/accounts/bleepbloop",
OriginAccount: nil,
TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A",
TargetAccount: nil,
StatusID: "01FE96NBPNJNY26730FT6GZTFE",
Status: nil,
}
}
type MentionValidateTestSuite struct {
suite.Suite
}
func (suite *MentionValidateTestSuite) TestValidateMentionHappyPath() {
// no problem here
m := happyMention()
err := validate.Struct(m)
suite.NoError(err)
}
func (suite *MentionValidateTestSuite) TestValidateMentionBadID() {
m := happyMention()
m.ID = ""
err := validate.Struct(m)
suite.EqualError(err, "Key: 'Mention.ID' Error:Field validation for 'ID' failed on the 'required' tag")
m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'Mention.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *MentionValidateTestSuite) TestValidateMentionAccountURI() {
m := happyMention()
m.OriginAccountURI = ""
err := validate.Struct(m)
suite.EqualError(err, "Key: 'Mention.OriginAccountURI' Error:Field validation for 'OriginAccountURI' failed on the 'url' tag")
m.OriginAccountURI = "---------------------------"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'Mention.OriginAccountURI' Error:Field validation for 'OriginAccountURI' failed on the 'url' tag")
}
func (suite *MentionValidateTestSuite) TestValidateMentionDodgyStatusID() {
m := happyMention()
m.StatusID = "9HZJ76B6VXSKF"
err := validate.Struct(m)
suite.EqualError(err, "Key: 'Mention.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
m.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'Mention.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
}
func (suite *MentionValidateTestSuite) TestValidateMentionNoCreatedAt() {
m := happyMention()
m.CreatedAt = time.Time{}
err := validate.Struct(m)
suite.NoError(err)
}
func TestMentionValidateTestSuite(t *testing.T) {
suite.Run(t, new(MentionValidateTestSuite))
}

View file

@ -1,97 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyNotification() *gtsmodel.Notification {
return &gtsmodel.Notification{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
NotificationType: gtsmodel.NotificationFave,
OriginAccountID: "01FE96MAE58MXCE5C4SSMEMCEK",
OriginAccount: nil,
TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A",
TargetAccount: nil,
StatusID: "01FE96NBPNJNY26730FT6GZTFE",
Status: nil,
}
}
type NotificationValidateTestSuite struct {
suite.Suite
}
func (suite *NotificationValidateTestSuite) TestValidateNotificationHappyPath() {
// no problem here
n := happyNotification()
err := validate.Struct(n)
suite.NoError(err)
}
func (suite *NotificationValidateTestSuite) TestValidateNotificationBadID() {
n := happyNotification()
n.ID = ""
err := validate.Struct(n)
suite.EqualError(err, "Key: 'Notification.ID' Error:Field validation for 'ID' failed on the 'required' tag")
n.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(n)
suite.EqualError(err, "Key: 'Notification.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *NotificationValidateTestSuite) TestValidateNotificationStatusID() {
n := happyNotification()
n.StatusID = ""
err := validate.Struct(n)
suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'required_if' tag")
n.StatusID = "9HZJ76B6VXSKF"
err = validate.Struct(n)
suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
n.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!"
err = validate.Struct(n)
suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
n.StatusID = ""
n.NotificationType = gtsmodel.NotificationFollowRequest
err = validate.Struct(n)
suite.NoError(err)
}
func (suite *NotificationValidateTestSuite) TestValidateNotificationNoCreatedAt() {
n := happyNotification()
n.CreatedAt = time.Time{}
err := validate.Struct(n)
suite.NoError(err)
}
func TestNotificationValidateTestSuite(t *testing.T) {
suite.Run(t, new(NotificationValidateTestSuite))
}

View file

@ -1,87 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyRouterSession() *gtsmodel.RouterSession {
return &gtsmodel.RouterSession{
ID: "01FE91RJR88PSEEE30EV35QR8N",
Auth: []byte("12345678901234567890123456789012"),
Crypt: []byte("12345678901234567890123456789012"),
}
}
type RouterSessionValidateTestSuite struct {
suite.Suite
}
func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionHappyPath() {
// no problem here
r := happyRouterSession()
err := validate.Struct(r)
suite.NoError(err)
}
func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionAuth() {
r := happyRouterSession()
// remove auth struct
r.Auth = nil
err := validate.Struct(r)
suite.EqualError(err, "Key: 'RouterSession.Auth' Error:Field validation for 'Auth' failed on the 'required' tag")
// auth bytes too long
r.Auth = []byte("1234567890123456789012345678901234567890")
err = validate.Struct(r)
suite.EqualError(err, "Key: 'RouterSession.Auth' Error:Field validation for 'Auth' failed on the 'len' tag")
// auth bytes too short
r.Auth = []byte("12345678901")
err = validate.Struct(r)
suite.EqualError(err, "Key: 'RouterSession.Auth' Error:Field validation for 'Auth' failed on the 'len' tag")
}
func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionCrypt() {
r := happyRouterSession()
// remove crypt struct
r.Crypt = nil
err := validate.Struct(r)
suite.EqualError(err, "Key: 'RouterSession.Crypt' Error:Field validation for 'Crypt' failed on the 'required' tag")
// crypt bytes too long
r.Crypt = []byte("1234567890123456789012345678901234567890")
err = validate.Struct(r)
suite.EqualError(err, "Key: 'RouterSession.Crypt' Error:Field validation for 'Crypt' failed on the 'len' tag")
// crypt bytes too short
r.Crypt = []byte("12345678901")
err = validate.Struct(r)
suite.EqualError(err, "Key: 'RouterSession.Crypt' Error:Field validation for 'Crypt' failed on the 'len' tag")
}
func TestRouterSessionValidateTestSuite(t *testing.T) {
suite.Run(t, new(RouterSessionValidateTestSuite))
}

View file

@ -1,160 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
"github.com/superseriousbusiness/gotosocial/testrig"
)
func happyStatus() *gtsmodel.Status {
return &gtsmodel.Status{
ID: "01FEBBH6NYDG87NK6A6EC543ED",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
URI: "https://example.org/users/test_user/statuses/01FEBBH6NYDG87NK6A6EC543ED",
URL: "https://example.org/@test_user/01FEBBH6NYDG87NK6A6EC543ED",
Content: "<p>Test status! #hello</p>",
AttachmentIDs: []string{"01FEBBKZBY9H5FEP3PHVVAAGN1", "01FEBBM7S2R4WT6WWW22KN1PWE"},
Attachments: nil,
TagIDs: []string{"01FEBBNBMBSN1FESMZ1TCXNWYP"},
Tags: nil,
MentionIDs: nil,
Mentions: nil,
EmojiIDs: nil,
Emojis: nil,
Local: testrig.TrueBool(),
AccountID: "01FEBBQ4KEP3824WW61MF52638",
Account: nil,
AccountURI: "https://example.org/users/test_user",
InReplyToID: "",
InReplyToURI: "",
InReplyToAccountID: "",
InReplyTo: nil,
InReplyToAccount: nil,
BoostOfID: "",
BoostOfAccountID: "",
BoostOf: nil,
BoostOfAccount: nil,
ContentWarning: "hello world test post",
Visibility: gtsmodel.VisibilityPublic,
Sensitive: testrig.FalseBool(),
Language: "en",
CreatedWithApplicationID: "01FEBBZHF4GFVRXSJVXD0JTZZ2",
CreatedWithApplication: nil,
Federated: testrig.TrueBool(),
Boostable: testrig.TrueBool(),
Replyable: testrig.TrueBool(),
Likeable: testrig.TrueBool(),
ActivityStreamsType: ap.ObjectNote,
Text: "Test status! #hello",
}
}
type StatusValidateTestSuite struct {
suite.Suite
}
func (suite *StatusValidateTestSuite) TestValidateStatusHappyPath() {
// no problem here
s := happyStatus()
err := validate.Struct(s)
suite.NoError(err)
}
func (suite *StatusValidateTestSuite) TestValidateStatusBadID() {
s := happyStatus()
s.ID = ""
err := validate.Struct(s)
suite.EqualError(err, "Key: 'Status.ID' Error:Field validation for 'ID' failed on the 'required' tag")
s.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(s)
suite.EqualError(err, "Key: 'Status.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *StatusValidateTestSuite) TestValidateStatusAttachmentIDs() {
s := happyStatus()
s.AttachmentIDs[0] = ""
err := validate.Struct(s)
suite.EqualError(err, "Key: 'Status.AttachmentIDs[0]' Error:Field validation for 'AttachmentIDs[0]' failed on the 'ulid' tag")
s.AttachmentIDs[0] = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(s)
suite.EqualError(err, "Key: 'Status.AttachmentIDs[0]' Error:Field validation for 'AttachmentIDs[0]' failed on the 'ulid' tag")
s.AttachmentIDs[1] = ""
err = validate.Struct(s)
suite.EqualError(err, "Key: 'Status.AttachmentIDs[0]' Error:Field validation for 'AttachmentIDs[0]' failed on the 'ulid' tag\nKey: 'Status.AttachmentIDs[1]' Error:Field validation for 'AttachmentIDs[1]' failed on the 'ulid' tag")
s.AttachmentIDs = []string{}
err = validate.Struct(s)
suite.NoError(err)
s.AttachmentIDs = nil
err = validate.Struct(s)
suite.NoError(err)
}
func (suite *StatusValidateTestSuite) TestStatusApplicationID() {
s := happyStatus()
s.CreatedWithApplicationID = ""
err := validate.Struct(s)
suite.EqualError(err, "Key: 'Status.CreatedWithApplicationID' Error:Field validation for 'CreatedWithApplicationID' failed on the 'required_if' tag")
s.Local = testrig.FalseBool()
err = validate.Struct(s)
suite.NoError(err)
}
func (suite *StatusValidateTestSuite) TestValidateStatusReplyFields() {
s := happyStatus()
s.InReplyToAccountID = "01FEBCTP6DN7961PN81C3DVM4N "
err := validate.Struct(s)
suite.EqualError(err, "Key: 'Status.InReplyToID' Error:Field validation for 'InReplyToID' failed on the 'required_with' tag\nKey: 'Status.InReplyToURI' Error:Field validation for 'InReplyToURI' failed on the 'required_with' tag\nKey: 'Status.InReplyToAccountID' Error:Field validation for 'InReplyToAccountID' failed on the 'ulid' tag")
s.InReplyToAccountID = "01FEBCTP6DN7961PN81C3DVM4N"
err = validate.Struct(s)
suite.EqualError(err, "Key: 'Status.InReplyToID' Error:Field validation for 'InReplyToID' failed on the 'required_with' tag\nKey: 'Status.InReplyToURI' Error:Field validation for 'InReplyToURI' failed on the 'required_with' tag")
s.InReplyToURI = "https://example.org/users/mmbop/statuses/aaaaaaaa"
err = validate.Struct(s)
suite.EqualError(err, "Key: 'Status.InReplyToID' Error:Field validation for 'InReplyToID' failed on the 'required_with' tag")
s.InReplyToID = "not a valid ulid"
err = validate.Struct(s)
suite.EqualError(err, "Key: 'Status.InReplyToID' Error:Field validation for 'InReplyToID' failed on the 'ulid' tag")
s.InReplyToID = "01FEBD07E72DEY6YB9K10ZA6ST"
err = validate.Struct(s)
suite.NoError(err)
}
func TestStatusValidateTestSuite(t *testing.T) {
suite.Run(t, new(StatusValidateTestSuite))
}

View file

@ -1,87 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyStatusBookmark() *gtsmodel.StatusBookmark {
return &gtsmodel.StatusBookmark{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
AccountID: "01FE96MAE58MXCE5C4SSMEMCEK",
Account: nil,
TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A",
TargetAccount: nil,
StatusID: "01FE96NBPNJNY26730FT6GZTFE",
Status: nil,
}
}
type StatusBookmarkValidateTestSuite struct {
suite.Suite
}
func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkHappyPath() {
// no problem here
s := happyStatusBookmark()
err := validate.Struct(s)
suite.NoError(err)
}
func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkBadID() {
s := happyStatusBookmark()
s.ID = ""
err := validate.Struct(s)
suite.EqualError(err, "Key: 'StatusBookmark.ID' Error:Field validation for 'ID' failed on the 'required' tag")
s.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(s)
suite.EqualError(err, "Key: 'StatusBookmark.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkDodgyStatusID() {
s := happyStatusBookmark()
s.StatusID = "9HZJ76B6VXSKF"
err := validate.Struct(s)
suite.EqualError(err, "Key: 'StatusBookmark.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
s.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!"
err = validate.Struct(s)
suite.EqualError(err, "Key: 'StatusBookmark.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
}
func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkNoCreatedAt() {
s := happyStatusBookmark()
s.CreatedAt = time.Time{}
err := validate.Struct(s)
suite.NoError(err)
}
func TestStatusBookmarkValidateTestSuite(t *testing.T) {
suite.Run(t, new(StatusBookmarkValidateTestSuite))
}

View file

@ -1,100 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyStatusFave() *gtsmodel.StatusFave {
return &gtsmodel.StatusFave{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
AccountID: "01FE96MAE58MXCE5C4SSMEMCEK",
Account: nil,
TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A",
TargetAccount: nil,
StatusID: "01FE96NBPNJNY26730FT6GZTFE",
Status: nil,
URI: "https://example.org/users/user1/activity/faves/01FE91RJR88PSEEE30EV35QR8N",
}
}
type StatusFaveValidateTestSuite struct {
suite.Suite
}
func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveHappyPath() {
// no problem here
f := happyStatusFave()
err := validate.Struct(f)
suite.NoError(err)
}
func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveBadID() {
f := happyStatusFave()
f.ID = ""
err := validate.Struct(f)
suite.EqualError(err, "Key: 'StatusFave.ID' Error:Field validation for 'ID' failed on the 'required' tag")
f.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(f)
suite.EqualError(err, "Key: 'StatusFave.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveDodgyStatusID() {
f := happyStatusFave()
f.StatusID = "9HZJ76B6VXSKF"
err := validate.Struct(f)
suite.EqualError(err, "Key: 'StatusFave.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
f.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!"
err = validate.Struct(f)
suite.EqualError(err, "Key: 'StatusFave.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
}
func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveNoCreatedAt() {
f := happyStatusFave()
f.CreatedAt = time.Time{}
err := validate.Struct(f)
suite.NoError(err)
}
func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveNoURI() {
f := happyStatusFave()
f.URI = ""
err := validate.Struct(f)
suite.EqualError(err, "Key: 'StatusFave.URI' Error:Field validation for 'URI' failed on the 'required' tag")
f.URI = "this-is-not-a-valid-url"
err = validate.Struct(f)
suite.EqualError(err, "Key: 'StatusFave.URI' Error:Field validation for 'URI' failed on the 'url' tag")
}
func TestStatusFaveValidateTestSuite(t *testing.T) {
suite.Run(t, new(StatusFaveValidateTestSuite))
}

View file

@ -1,87 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyStatusMute() *gtsmodel.StatusMute {
return &gtsmodel.StatusMute{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
AccountID: "01FE96MAE58MXCE5C4SSMEMCEK",
Account: nil,
TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A",
TargetAccount: nil,
StatusID: "01FE96NBPNJNY26730FT6GZTFE",
Status: nil,
}
}
type StatusMuteValidateTestSuite struct {
suite.Suite
}
func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteHappyPath() {
// no problem here
m := happyStatusMute()
err := validate.Struct(m)
suite.NoError(err)
}
func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteBadID() {
m := happyStatusMute()
m.ID = ""
err := validate.Struct(m)
suite.EqualError(err, "Key: 'StatusMute.ID' Error:Field validation for 'ID' failed on the 'required' tag")
m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'StatusMute.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteDodgyStatusID() {
m := happyStatusMute()
m.StatusID = "9HZJ76B6VXSKF"
err := validate.Struct(m)
suite.EqualError(err, "Key: 'StatusMute.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
m.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!"
err = validate.Struct(m)
suite.EqualError(err, "Key: 'StatusMute.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag")
}
func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteNoCreatedAt() {
m := happyStatusMute()
m.CreatedAt = time.Time{}
err := validate.Struct(m)
suite.NoError(err)
}
func TestStatusMuteValidateTestSuite(t *testing.T) {
suite.Run(t, new(StatusMuteValidateTestSuite))
}

View file

@ -1,67 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate
import (
"reflect"
"github.com/go-playground/validator/v10"
"github.com/superseriousbusiness/gotosocial/internal/regexes"
)
var v *validator.Validate
func ulidValidator(fl validator.FieldLevel) bool {
field := fl.Field()
switch field.Kind() {
case reflect.String:
return regexes.ULID.MatchString(field.String())
default:
return false
}
}
func init() {
v = validator.New()
if err := v.RegisterValidation("ulid", ulidValidator); err != nil {
panic(err)
}
}
// Struct validates the passed struct, returning validator.ValidationErrors if invalid, or nil if OK.
func Struct(s interface{}) error {
return processValidationError(v.Struct(s))
}
func processValidationError(err error) error {
if err == nil {
return nil
}
if ive, ok := err.(*validator.InvalidValidationError); ok {
panic(ive)
}
valErr, ok := err.(validator.ValidationErrors)
if !ok {
panic("*validator.InvalidValidationError could not be coerced to validator.ValidationErrors")
}
return valErr
}

View file

@ -1,70 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
type ValidateTestSuite struct {
suite.Suite
}
func (suite *ValidateTestSuite) TestValidateNilPointer() {
var nilUser *gtsmodel.User
suite.Panics(func() {
validate.Struct(nilUser)
})
}
func (suite *ValidateTestSuite) TestValidatePointer() {
user := &gtsmodel.User{}
err := validate.Struct(user)
suite.EqualError(err, "Key: 'User.ID' Error:Field validation for 'ID' failed on the 'required' tag\nKey: 'User.AccountID' Error:Field validation for 'AccountID' failed on the 'required' tag\nKey: 'User.EncryptedPassword' Error:Field validation for 'EncryptedPassword' failed on the 'required' tag\nKey: 'User.UnconfirmedEmail' Error:Field validation for 'UnconfirmedEmail' failed on the 'required_without' tag")
}
func (suite *ValidateTestSuite) TestValidateNil() {
suite.Panics(func() {
validate.Struct(nil)
})
}
func (suite *ValidateTestSuite) TestValidateWeirdULID() {
type a struct {
ID bool `validate:"required,ulid"`
}
err := validate.Struct(a{ID: true})
suite.Error(err)
}
func (suite *ValidateTestSuite) TestValidateNotStruct() {
type aaaaaaa string
aaaaaa := aaaaaaa("aaaa")
suite.Panics(func() {
validate.Struct(aaaaaa)
})
}
func TestValidateTestSuite(t *testing.T) {
suite.Run(t, new(ValidateTestSuite))
}

View file

@ -1,98 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
func happyToken() *gtsmodel.Token {
return &gtsmodel.Token{
ID: "01FE91RJR88PSEEE30EV35QR8N",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
ClientID: "01FEEDMF6C0QD589MRK7919Z0R",
UserID: "01FEK0BFJKYXB4Y51RBQ7P5P79",
RedirectURI: "oauth2redirect://com.keylesspalace.tusky/",
Scope: "read write follow",
}
}
type TokenValidateTestSuite struct {
suite.Suite
}
func (suite *TokenValidateTestSuite) TestValidateTokenHappyPath() {
// no problem here
t := happyToken()
err := validate.Struct(t)
suite.NoError(err)
}
func (suite *TokenValidateTestSuite) TestValidateTokenBadID() {
t := happyToken()
t.ID = ""
err := validate.Struct(t)
suite.EqualError(err, "Key: 'Token.ID' Error:Field validation for 'ID' failed on the 'required' tag")
t.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB"
err = validate.Struct(t)
suite.EqualError(err, "Key: 'Token.ID' Error:Field validation for 'ID' failed on the 'ulid' tag")
}
func (suite *TokenValidateTestSuite) TestValidateTokenNoCreatedAt() {
t := happyToken()
t.CreatedAt = time.Time{}
err := validate.Struct(t)
suite.NoError(err)
}
func (suite *TokenValidateTestSuite) TestValidateTokenRedirectURI() {
t := happyToken()
t.RedirectURI = "invalid-uri"
err := validate.Struct(t)
suite.EqualError(err, "Key: 'Token.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'uri' tag")
t.RedirectURI = ""
err = validate.Struct(t)
suite.EqualError(err, "Key: 'Token.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'required' tag")
t.RedirectURI = "urn:ietf:wg:oauth:2.0:oob"
err = validate.Struct(t)
suite.NoError(err)
}
func (suite *TokenValidateTestSuite) TestValidateTokenScope() {
t := happyToken()
t.Scope = ""
err := validate.Struct(t)
suite.EqualError(err, "Key: 'Token.Scope' Error:Field validation for 'Scope' failed on the 'required' tag")
}
func TestTokenValidateTestSuite(t *testing.T) {
suite.Run(t, new(TokenValidateTestSuite))
}

View file

@ -1,134 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package validate_test
import (
"net"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/validate"
"github.com/superseriousbusiness/gotosocial/testrig"
)
func happyUser() *gtsmodel.User {
return &gtsmodel.User{
ID: "01FE8TTK9F34BR0KG7639AJQTX",
Email: "whatever@example.org",
AccountID: "01FE8TWA7CN8J7237K5DFS1RY5",
Account: nil,
EncryptedPassword: "$2y$10$tkRapNGW.RWkEuCMWdgArunABFvsPGRvFQY3OibfSJo0RDL3z8WfC",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
SignUpIP: net.ParseIP("128.64.32.16"),
CurrentSignInAt: time.Now(),
CurrentSignInIP: net.ParseIP("128.64.32.16"),
LastSignInAt: time.Now(),
LastSignInIP: net.ParseIP("128.64.32.16"),
SignInCount: 0,
InviteID: "",
ChosenLanguages: []string{},
FilteredLanguages: []string{},
Locale: "en",
CreatedByApplicationID: "01FE8Y5EHMWCA1MHMTNHRVZ1X4",
CreatedByApplication: nil,
LastEmailedAt: time.Now(),
ConfirmationToken: "",
ConfirmedAt: time.Now(),
ConfirmationSentAt: time.Time{},
UnconfirmedEmail: "",
Moderator: testrig.FalseBool(),
Admin: testrig.FalseBool(),
Disabled: testrig.FalseBool(),
Approved: testrig.TrueBool(),
}
}
type UserValidateTestSuite struct {
suite.Suite
}
func (suite *UserValidateTestSuite) TestValidateUserHappyPath() {
// no problem here
u := happyUser()
err := validate.Struct(u)
suite.NoError(err)
}
func (suite *UserValidateTestSuite) TestValidateUserNoID() {
// user has no id set
u := happyUser()
u.ID = ""
err := validate.Struct(u)
suite.EqualError(err, "Key: 'User.ID' Error:Field validation for 'ID' failed on the 'required' tag")
}
func (suite *UserValidateTestSuite) TestValidateUserNoEmail() {
// user has no email or unconfirmed email set
u := happyUser()
u.Email = ""
err := validate.Struct(u)
suite.EqualError(err, "Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required_with' tag\nKey: 'User.UnconfirmedEmail' Error:Field validation for 'UnconfirmedEmail' failed on the 'required_without' tag")
}
func (suite *UserValidateTestSuite) TestValidateUserOnlyUnconfirmedEmail() {
// user has only UnconfirmedEmail but ConfirmedAt is set
u := happyUser()
u.Email = ""
u.UnconfirmedEmail = "whatever@example.org"
err := validate.Struct(u)
suite.EqualError(err, "Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required_with' tag")
}
func (suite *UserValidateTestSuite) TestValidateUserOnlyUnconfirmedEmailOK() {
// user has only UnconfirmedEmail and ConfirmedAt is not set
u := happyUser()
u.Email = ""
u.UnconfirmedEmail = "whatever@example.org"
u.ConfirmedAt = time.Time{}
err := validate.Struct(u)
suite.NoError(err)
}
func (suite *UserValidateTestSuite) TestValidateUserNoConfirmedAt() {
// user has Email but no ConfirmedAt
u := happyUser()
u.ConfirmedAt = time.Time{}
err := validate.Struct(u)
suite.EqualError(err, "Key: 'User.ConfirmedAt' Error:Field validation for 'ConfirmedAt' failed on the 'required_with' tag")
}
func (suite *UserValidateTestSuite) TestValidateUserUnlikelySignInCount() {
// user has Email but no ConfirmedAt
u := happyUser()
u.SignInCount = -69
err := validate.Struct(u)
suite.EqualError(err, "Key: 'User.SignInCount' Error:Field validation for 'SignInCount' failed on the 'min' tag")
}
func TestUserValidateTestSuite(t *testing.T) {
suite.Run(t, new(UserValidateTestSuite))
}