types

package
v0.0.0-...-b6e0fdc Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 2, 2024 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const (
	MAX_FILE_SIZE_IMAGE = 8 * 1024 * 1024  // 8MB
	MAX_FILE_SIZE_VIDEO = 24 * 1024 * 1024 // 24MB

	FILE_NAME_CHAR_SET = "abcdefghijkmnpqrstuvwxyz123456789-_"
	FILE_NAME_LENGTH   = 32
)

Variables

View Source
var (
	PUBLIC_ARTICLE_FIELDS = []string{"title", "body", "slug", "author", "co_authors", "status", "tags", "created_at", "updated_at", "deleted_at"}

	// char sets
	// min/max only apply to randomly generated slugs. reserved/specified slugs can be up to 128 characters.
	ARTICLE_SLUG_CHAR_SET = "abcdefghijklmnopqrstuvwxyz0123456789"
	ARTICLE_MAX_SLUG_LEN  = 16
	ARTICLE_MIN_SLUG_LEN  = 10
)
View Source
var (
	// character sets
	IDENTITY_CHAR_SET = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789-_"

	// permissions
	PUBLIC_IDENTITY_FIELDS   = []string{"name", "style", "role", "status", "created_at", "updated_at", "deleted_at"}
	PERSONAL_IDENTITY_FIELDS = []string{"_id", "account"}

	// styles
	IDENTITY_STYLE_PREFIXES = []string{"filled", "ghost", "soft", "glass"}
	IDENTITY_STYLE_SUFFIXES = []string{"primary", "secondary", "tertiary", "success", "warning", "error", "surface"}
)
View Source
var (
	// permissions
	PUBLIC_POST_FIELDS = []string{"post_number", "body", "assets", "creator", "board", "thread", "created_at", "updated_at", "deleted_at"}
	ADMIN_POST_FIELDS  = []string{"_id", "account"}
)
View Source
var (
	// measurements of time
	SECONDS_IN_MINUTE = 60
	MINUTES_IN_HOUR   = 60
	HOURS_IN_DAY      = 24

	DAYS_IN_WEEK = 7
	DAYS_IN_YEAR = 365

	SECONDS_IN_HOUR = SECONDS_IN_MINUTE * MINUTES_IN_HOUR
	SECONDS_IN_DAY  = SECONDS_IN_HOUR * HOURS_IN_DAY
	SECONDS_IN_WEEK = SECONDS_IN_DAY * DAYS_IN_WEEK

	MINUTES_IN_DAY  = MINUTES_IN_HOUR * HOURS_IN_DAY
	MINUTES_IN_WEEK = MINUTES_IN_DAY * DAYS_IN_WEEK

	HOURS_IN_WEEK = HOURS_IN_DAY * DAYS_IN_WEEK
	HOURS_IN_YEAR = HOURS_IN_DAY * DAYS_IN_YEAR

	// permissions
	PUBLIC_SESSION_FIELDS   = []string{"created_at", "updated_at", "deleted_at"}
	PERSONAL_SESSION_FIELDS = []string{"_id", "account_id", "session_id", "expires"}
)
View Source
var (
	PostLinkRegex = map[string]*regexp.Regexp{
		"post-internal-thread":  regexp.MustCompile(`(?m)>>([[:digit:]]{1,9})<`),
		"thread-internal-board": regexp.MustCompile(`(?m)>>([[:alnum:]]{8,12})<`),
		"post-internal-board":   regexp.MustCompile(`(?m)>>([[:alnum:]]{8,12})/([[:digit:]]{1,9})<`),
		"thread-external-board": regexp.MustCompile(`(?m)>>([[:alpha:]]{2,5})/([[:alnum:]]{8,12})<`),
		"post-external-board":   regexp.MustCompile(`(?m)>>([[:alpha:]]{2,5})/([[:alnum:]]{8,12})/([[:digit:]]{1,9})<`),
	}
	// paragraph delimiting patterns
	CtrlCharReplace     = regexp.MustCompile(`(?m)[[:cntrl:]]`)
	ExcessiveNewLineFix = regexp.MustCompile(`(?m)\n{2,}`)
	QuoteWrap           = regexp.MustCompile(`(?ms)>"(.*)"`)

	// whitespace fixes
	LineStartEndSpaceFix = regexp.MustCompile(`(?m)^[[:blank:]]+|[[:blank:]]+$`)
	ExtraSpaceLimit      = regexp.MustCompile(`(?m)[[:blank:]]+`)
)
View Source
var (
	// permissions
	PUBLIC_THREAD_FIELDS = []string{"title", "body", "slug", "board", "creator", "posts", "mods", "status", "tags", "created_at", "updated_at", "deleted_at"}
	MOD_THREAD_FIELDS    = []string{"flags"}
	ADMIN_THREAD_FIELDS  = []string{"_id", "account"}

	// character sets
	THREAD_SLUG_CHAR_SET = "abcdefghijklmnopqrstuvwxyz0123456789"
	THREAD_MIN_SLUG_LEN  = 8
	THREAD_MAX_SLUG_LEN  = 12
)
View Source
var (
	// permissions
	PUBLIC_BOARD_FIELDS = []string{"title", "short", "description", "threads", "created_at", "updated_at", "deleted_at"}
)

Status codes mapped to their respective ServerError

Functions

func FindFilterIdentityInThread

func FindFilterIdentityInThread(thread_id, account_id primitive.ObjectID) bson.D

returns a filter object that resolves an identity from a particular thread using specified account

func GetFileChecksumMD5

func GetFileChecksumMD5(filename string) (string, error)

returns an md5 checksum of the file located at filename

func GetFileChecksumSHA256

func GetFileChecksumSHA256(filename string) (string, error)

returns an sha256 checksum of the file located at filename

func GetFileSize

func GetFileSize(filename string) (int64, error)

returns the size of the file located at filename

func NewArticleSlug

func NewArticleSlug() string

func NewFileName

func NewFileName() (string, error)

func NewIdentityStyle

func NewIdentityStyle() string

func NewThreadSlug

func NewThreadSlug() string

func RequestLogger

func RequestLogger(rc *RequestCtx)

logs the request to the console

func UploadFileToSpaces

func UploadFileToSpaces(tasset *utils.TempAsset) (string, error)

upload temp file to spaces and return the url

Types

type APIError

type APIError struct {
	Status  int
	Message string
}

This should be the ONLY error type a client receives. Ever. Read top of file for more info.

func ErrorConflict

func ErrorConflict(msg string) APIError

New Conflict Error

func ErrorInvalid

func ErrorInvalid(what string) APIError

New Invalid Error - accepts a string of what was invalid

func ErrorNotFound

func ErrorNotFound(resource string) APIError

New Not Found Error - accepts a string of the resource that was not found

func ErrorUnauthorized

func ErrorUnauthorized() APIError

New Unauthorized Error

func ErrorUnexpected

func ErrorUnexpected() APIError

New Unexpected Error

func ErrorUnsupported

func ErrorUnsupported() APIError

New Unsupported Method Error

func NewAPIError

func NewAPIError(status int, message string) *APIError

Creates a new APIError with the given status and message

func (APIError) Bson

func (e APIError) Bson() bson.M

formats into bson (json likeish) for response

func (APIError) Error

func (e APIError) Error() string

implements the error interface

type APIResource

type APIResource string

These define the valid resource paths for the API directly after root.

const (
	Resource_Account  APIResource = "account"
	Resource_Article  APIResource = "article"
	Resource_Board    APIResource = "board"
	Resource_Identity APIResource = "identity"
	Resource_Post     APIResource = "post"
	Resource_Thread   APIResource = "thread"
	Resource_Session  APIResource = "session"
	Resource_Asset    APIResource = "asset"
)

APIResource constants will by default be it's singular form. These will be modified when necessary to their plural forms to match the database collection names.

type Account

type Account struct {
	ID       primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
	Username string             `bson:"username,omitempty" json:"username"`
	Email    string             `bson:"email,omitempty" json:"email"`

	Role   AccountRole   `bson:"role" json:"role"`
	Status AccountStatus `bson:"status" json:"status"`

	Password string `json:"password_hash" bson:"password_hash"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

func NewAccount

func NewAccount() *Account

Creates a new Account object with some values set by default

func (*Account) CLFormat

func (a *Account) CLFormat() bson.M

ClientFormatter implementation

func (*Account) IsAdmin

func (a *Account) IsAdmin() bool

func (*Account) IsMod

func (a *Account) IsMod() bool

func (*Account) IsStaff

func (a *Account) IsStaff() bool

type AccountCtx

type AccountCtx struct {
	Session        *Session    `json:"session,omitempty"`
	Account        *Account    `json:"account,omitempty"`
	ExpiredSession bool        `json:"expired_session,omitempty"`
	Role           AccountRole `json:"-"`
}

context of the requesting user. if unable to resolve a user pointers will be nil

func NewAccountCtx

func NewAccountCtx() *AccountCtx

creates a new user context with default values and a public role

type AccountRole

type AccountRole string
const (
	AccountRoleUnknown AccountRole = "unknown"
	AccountRolePublic  AccountRole = "public"
	AccountRoleUser    AccountRole = "user"
	AccountRoleMod     AccountRole = "mod"
	AccountRoleAdmin   AccountRole = "admin"
)

type AccountStatus

type AccountStatus string
const (
	AccountStatusUnknown   AccountStatus = "unknown"
	AccountStatusActive    AccountStatus = "active"
	AccountStatusSuspended AccountStatus = "suspended"
	AccountStatusBanned    AccountStatus = "banned"
	AccountStatusDeleted   AccountStatus = "deleted"
)

type Article

type Article struct {
	ID primitive.ObjectID `json:"_id" bson:"_id,omitempty"`

	AuthorID  primitive.ObjectID   `json:"author" bson:"author"`         // ArticleAuthor id
	CoAuthors []primitive.ObjectID `json:"co_authors" bson:"co_authors"` // ArticleAuthor id's

	Status     ArticleStatus `bson:"status" json:"status"`
	CommentRef int           `json:"comment_ref" bson:"comment_ref"`

	Comments []primitive.ObjectID `json:"comments" bson:"comments"`
	Assets   []primitive.ObjectID `json:"assets" bson:"assets"`

	Title string   `json:"title" bson:"title"`
	Body  string   `json:"body" bson:"body"`
	Slug  string   `json:"slug" bson:"slug"`
	Tags  []string `json:"tags" bson:"tags"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

func NewArticle

func NewArticle() *Article

func (*Article) CLFormat

func (a *Article) CLFormat() bson.M

ClientFormatter implementation

type ArticleAuthor

type ArticleAuthor struct {
	ID        primitive.ObjectID `json:"_id" bson:"_id,omitempty"`
	AuthorID  primitive.ObjectID `json:"author" bson:"author"`       // account id
	Anonymize bool               `json:"anonymize" bson:"anonymize"` // make author/coauthor with above id anonymous
}

func NewArticleAuthor

func NewArticleAuthor() *ArticleAuthor

type ArticleComment

type ArticleComment struct {
	ID         primitive.ObjectID `json:"_id" bson:"_id,omitempty"`
	AuthorID   primitive.ObjectID `json:"author" bson:"author"`                     // account id
	AuthorAnon bool               `json:"author_anonymous" bson:"author_anonymous"` // only admins/mods have the option

	CommentNumber int                  `json:"comment_number" bson:"comment_number"`
	Body          string               `json:"body" bson:"body"`
	Assets        []primitive.ObjectID `json:"assets" bson:"assets"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

func NewArticleComment

func NewArticleComment() *ArticleComment

type ArticleStatus

type ArticleStatus string
const (
	ArticleStatusDraft     ArticleStatus = "draft"
	ArticleStatusPublished ArticleStatus = "published"
	ArticleStatusArchived  ArticleStatus = "archived"
	ArticleStatusDeleted   ArticleStatus = "deleted"
)

type Asset

type Asset struct {
	ID primitive.ObjectID `json:"_id" bson:"_id"`

	SourceID  primitive.ObjectID `json:"source_id" bson:"source_id"`
	AccountID primitive.ObjectID `json:"account_id" bson:"account_id"`

	Description string   `json:"description" bson:"description"`
	FileName    string   `json:"file_name" bson:"file_name"`
	Tags        []string `json:"tags" bson:"tags"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

the client level asset struct - assets are aggregated from asset sources with all the information they need. if a user attempts to upload a file with an existing hash, instead it will not be uploaded and instead just an Asset reference will be created. This is to prevent duplicate files from being uploaded and to let them name their file whatever they want for their own organization.

func CloneAsset

func CloneAsset(asset *Asset, account_id primitive.ObjectID) *Asset

clone an existing asset but replace the account_id with a new one

func NewAsset

func NewAsset(src primitive.ObjectID, acct primitive.ObjectID) *Asset

creates a new asset, must provide an asset source id from which to derive and the account id of who uploaded it (or owns it)

type AssetSource

type AssetSource struct {
	ID primitive.ObjectID `json:"_id" bson:"_id"`

	Details *AssetSourceDetails `bson:"details" json:"details"`

	AssetType AssetType            `json:"asset_type" bson:"asset_type"`
	Uploaders []primitive.ObjectID `json:"uploaders" bson:"uploaders"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

the internal source asset. This is a source file from which all other assets are derived. This should NEVER be passed to the client unless they are an admin. This is for privacy and storage reasons. The derived asset should be populated with the information the client needs from this source asset.

func NewSourceAsset

func NewSourceAsset() *AssetSource

type AssetSourceDetails

type AssetSourceDetails struct {
	Avatar *FileCtx `json:"avatar" bson:"avatar"`
	Source *FileCtx `json:"source" bson:"source"`
}

type AssetType

type AssetType string

available asset types that can be uploaded - open for expansion

const (
	AssetTypeImage AssetType = "image"
	AssetTypeVideo AssetType = "video"
)

func (AssetType) String

func (at AssetType) String() string

type Board

type Board struct {
	ID primitive.ObjectID `bson:"_id,omitempty" json:"_id"`

	Title       string `bson:"title" json:"title"`
	Short       string `bson:"short" json:"short"` // short name for the board (used in URLs)
	Description string `bson:"description" json:"description"`

	Threads []primitive.ObjectID `bson:"threads,omitempty" json:"threads,omitempty"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`

	PostRef uint64 `bson:"post_ref" json:"post_ref"`
}

func NewBoard

func NewBoard() *Board

Creates a new board with an ID and other default values.

type ClientFormatter

type ClientFormatter interface {
	CLFormat() bson.M
}

format and strip sensitive data from a struct for sending to the client

type FileCtx

type FileCtx struct {
	ServerFileName string `json:"server_file_name" bson:"server_file_name"`
	Height         uint16 `json:"height" bson:"height"`
	Width          uint16 `json:"width" bson:"width"`
	FileSize       uint32 `json:"file_size" bson:"file_size"`
	URL            string `json:"url" bson:"url"`
	Extension      string `json:"extension" bson:"extension"`
	HashMD5        string `json:"hash_md5" bson:"hash_md5"`
	HashSHA256     string `json:"hash_sha256" bson:"hash_sha256"`
}

Details about the file, since files can have avatar and source files this is abstracted out. Both may not need all of these.

func NewFileCtx

func NewFileCtx() *FileCtx

a new file context

type FileUploadDetails

type FileUploadDetails struct {
	AssetType AssetType
	LocalID   string
	FileSize  uint32
	Height    int
	Width     int
}

func ParseFormFileDetails

func ParseFormFileDetails(rq *http.Request) *FileUploadDetails

type HashMethod

type HashMethod string
const (
	HashMethodMD5    HashMethod = "md5"
	HashMethodSHA256 HashMethod = "sha256"
)

func (HashMethod) String

func (hm HashMethod) String() string

type Identity

type Identity struct {
	ID      primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
	Account primitive.ObjectID `bson:"account,omitempty" json:"account"`

	Name  string `bson:"name" json:"name"`
	Style string `bson:"style" json:"style"`

	Role   ThreadRole     `bson:"role" json:"role"`
	Status IdentityStatus `bson:"status" json:"status"`

	Thread primitive.ObjectID `bson:"thread,omitempty" json:"thread"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

func NewIdentity

func NewIdentity() *Identity

Creates a new Identity object with some values set by default

func (*Identity) CLFormat

func (i *Identity) CLFormat() bson.M

ClientFormatter implementation

type IdentityStatus

type IdentityStatus string
const (
	IdentityStatusUnknown   IdentityStatus = "unknown"
	IdentityStatusActive    IdentityStatus = "active"
	IdentityStatusSuspended IdentityStatus = "suspended"
	IdentityStatusBanned    IdentityStatus = "banned"
	IdentityStatusDeleted   IdentityStatus = "deleted"
)

type PageCtx

type PageCtx struct {
	Current      int  `json:"current_page"`            // current page number
	Count        int  `json:"page_size"`               // number of records per page
	Pages        int  `json:"total_pages"`             // total number of pages
	Records      int  `json:"total_records,omitempty"` // total number of records (determines total number of pages)
	Last         bool `json:"last_page"`               // is this the last page
	Remainder    int  `json:"last_page_size"`          // number of records on the last page
	SendToClient bool `json:"-"`                       // should this be sent to the client
}

interim struct to hold pagination information

func NewPageCtx

func NewPageCtx() *PageCtx

creates a new page context with default values

func (*PageCtx) Update

func (p *PageCtx) Update(results int)

updates pagination info based on the query and it's results NOTE: this needs to be called after query resolver has been called otherwise the object will be empty

type Post

type Post struct {
	ID primitive.ObjectID `bson:"_id,omitempty" json:"_id"`

	PostNumber uint64             `bson:"post_number" json:"post_number"`
	Creator    primitive.ObjectID `bson:"creator" json:"creator"` // identity _id

	Body   string               `bson:"body" json:"body"`
	Assets []primitive.ObjectID `bson:"assets" json:"assets"`

	Board  primitive.ObjectID `bson:"board" json:"board"`
	Thread primitive.ObjectID `bson:"thread" json:"thread"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

func NewPost

func NewPost() *Post

Creates a new post with an ID and other default values.

func (*Post) CLFormat

func (p *Post) CLFormat() bson.M

ClientFormatter implementation

type PostLink string
const (
	PostInternalThread  PostLink = "post-internal-thread"
	ThreadInternalBoard PostLink = "thread-internal-board"
	PostInternalBoard   PostLink = "post-internal-board"
	ThreadExternalBoard PostLink = "thread-external-board"
	PostExternalBoard   PostLink = "post-external-board"
)

type QueryCtx

type QueryCtx struct {
	Sort                 string         // field to sort by
	Order                int            // 1 for ascending, -1 for descending
	Limit                int64          // number of records to return (size of page)
	Skip                 int64          // number of records to skip (page number * page size)
	Search               bson.D         // if we're searching for something
	Filter               bson.D         // if we're filtering for something
	UnhandledQueryParams map[string]any // any query params that we don't know what to do with
}

request query context information

func NewQueryCtx

func NewQueryCtx() *QueryCtx

creates a new query context with default values

type RUMAssetAttachment

type RUMAssetAttachment struct {
	SourceID    primitive.ObjectID `json:"source_id"`
	Description string             `json:"description,omitempty"`
	FileName    string             `json:"file_name,omitempty"`
	Tags        []string           `json:"tags,omitempty"`
}

func NewRUMAssetAttachment

func NewRUMAssetAttachment() *RUMAssetAttachment

type RUMComment

type RUMComment struct {
	Content       string               `json:"content"`
	Assets        []RUMAssetAttachment `json:"assets"`
	MakeAnonymous bool                 `json:"make_anonymous"`
}

func NewRUMComment

func NewRUMComment() *RUMComment

type RUMPost

type RUMPost struct {
	Content string               `json:"content"`
	Assets  []RUMAssetAttachment `json:"assets"`
}

same as thread but without a title

func NewRUMPost

func NewRUMPost() *RUMPost

type RUMSession

type RUMSession struct {
	SessionID string `json:"session"`
}

Session will be an ID in the cookie

type RUMThread

type RUMThread struct {
	Title   string               `json:"title"`
	Content string               `json:"content"`
	Assets  []RUMAssetAttachment `json:"assets"`
	Flags   RUMThreadFlags       `json:"flags"`
}

new thread requests come through the board/[short] POST route we already have the board because it's in the endpoint. these are the rest of the fields on a request to create a new thread

func NewRUMThread

func NewRUMThread() *RUMThread

type RUMThreadFlags

type RUMThreadFlags struct {
	NSFW     bool `json:"nsfw"`
	NSFL     bool `json:"nsfl"`
	REQMEDIA bool `json:"media_required"`
}

type RequestCtx

type RequestCtx struct {
	Request           *http.Request       // request
	Writer            http.ResponseWriter // writer
	Query             *QueryCtx           // the parsed query context (or nil if irrelevant/not yet parsed)
	Resource          APIResource         // this is the main subroute of the API, the first major path after root.
	Pagination        *PageCtx            `json:"pages"`   // pagination information
	Records           []bson.M            `json:"records"` // resource(s) we intend to return to the client
	Store             *Store
	AccountCtx        *AccountCtx
	TemplateStore     *TemplateStore
	UnresolvedAccount bool
	ResponseList      []bson.M
	ResponseData      bson.M
}

holds all of the resolved/parsed request details and info so that handlers can be more simple and focused.

func NewRequestCtx

func NewRequestCtx(w http.ResponseWriter, r *http.Request) *RequestCtx

creates a new request context - parses and resolves request details into the context in order for handlers to be more simple and focused

func (*RequestCtx) AddToResponseList

func (rc *RequestCtx) AddToResponseList(k string, v any)

adds the given key/value pair to the response list to be returned to the client when the response is finalized

func (*RequestCtx) AddToResponseListCLF

func (rc *RequestCtx) AddToResponseListCLF(k string, v ClientFormatter)

uses the ClientFormatter interface to send potentially sensitive data to the client by using the CLFormat() method to format the data for the client

func (*RequestCtx) Finalize

func (rc *RequestCtx) Finalize()

prepares the response list to be sent to the client

func (*RequestCtx) Resolve

func (rc *RequestCtx) Resolve() *RequestCtx

parse the request and populate each of the contexts with relevant information certain contexts must be done synchronously in a certain order to ensure the necessary data is available for the next context to be resolved

func (*RequestCtx) ResolveAccountCtx

func (rc *RequestCtx) ResolveAccountCtx()

resolves an account context for the request

func (*RequestCtx) UpdateStore

func (rc *RequestCtx) UpdateStore(s *Store)

updates the request context with the store

type RequestUnmarshaller

type RequestUnmarshaller interface {
	UnmarshalFromReqInto(*RequestCtx) error
}

type RoutingHandler

type RoutingHandler struct {
	Router *mux.Router
	Store  *Store
}

top level router with access to the store for database operations

func NewRoutingHandler

func NewRoutingHandler(s *Store) *RoutingHandler

type ServerCache

type ServerCache struct {
	Boards    map[string]*Board               // short -> Board
	Sessions  map[string]*Session             // session_id -> Session
	Accounts  map[primitive.ObjectID]*Account // _id -> Account
	StartedAt *time.Time
	EndedAt   *time.Time
}

High level server cache for frequently used data to avoid network calls

Boards are cached on server start, sessions and accounts are cached as they're accessed.

func NewServerCache

func NewServerCache() *ServerCache

type ServerError

type ServerError string
const (
	Error_Unexpected   ServerError = "unexpected server error"
	Error_NotFound     ServerError = "not found"
	Error_Invalid      ServerError = "invalid"
	Error_Unauthorized ServerError = "unauthorized"
	Error_Unsupported  ServerError = "unsupported method"
)

given strings for each error type

func (ServerError) String

func (se ServerError) String() string

type Session

type Session struct {
	ID primitive.ObjectID `bson:"_id,omitempty" json:"_id"`

	SessionID string             `bson:"session_id" json:"session_id"`
	AccountID primitive.ObjectID `bson:"account_id" json:"account_id"`
	Account   *Account           `bson:"account" json:"account"`

	Expires *time.Time `bson:"expires" json:"expires"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

func NewSession

func NewSession(account *Account) *Session

creates a new session for the given account

func (*Session) CLFormat

func (s *Session) CLFormat() bson.M

implements the ClientFormatter interface

func (*Session) IsExpired

func (s *Session) IsExpired() bool

is the session expired?

func (*Session) IsExpiringSoon

func (s *Session) IsExpiringSoon() bool

will the session expire within the next hour?

func (*Session) IsExpiryImminent

func (s *Session) IsExpiryImminent() bool

is the session expiring in the next 5 minutes?

type Store

type Store struct {
	Name      string
	Client    *mongo.Client
	DB        *mongo.Database
	Cache     *ServerCache
	StartedAt *time.Time
	EndedAt   *time.Time
}

Global data store initialized on server start

Store is initialized on server start and is referenced in all handlers. The RequestCtx for any particular handler will be updated with a reference to the store after it's called.

This is all to avoid circular dependencies while giving store access essentially everywhere.

func NewStore

func NewStore(dbname string) (*Store, error)

func (*Store) AssetHashCollisionResolver

func (s *Store) AssetHashCollisionResolver(queries ...primitive.D) (*AssetSource, error)

runs a list of queries provided (hash collision query strings from builder) returns an AssetSource of the first matching hash collision

func (*Store) CountResults

func (s *Store) CountResults(col string, filter bson.D) int64

Count Results - accepts a string of the collection name - accepts a bson.D of the filter - returns an int64 of the count

Counts the number of documents matching the given filter in the specified collection.
useful for pagination since when we query for results we're only receiving a subset of the total results.

func (*Store) DeleteSingle

func (s *Store) DeleteSingle(id primitive.ObjectID, col string) error

Delete a single Document - accepts a primitive.ObjectID of the document to be deleted - accepts a string of the collection name - returns an error if one occurs

func (*Store) FindAccountByUsernameOrEmail

func (s *Store) FindAccountByUsernameOrEmail(username string, email string) (*Account, error)

Find Account By Username or Email - accepts a string of the username - accepts a string of the email (optional - can be empty string) - returns a pointer to the account - returns an error if one occurs

Always queries the database since we don't index by username or email in the cache and it's not a frequently used query.
Email is optional, if it's empty it will use the username (first parameter) to search by both username and email.

func (*Store) FindAccountFromSession

func (s *Store) FindAccountFromSession(id string) (*Account, error)

Find Account By Session ID - accepts a string of the session id - returns a pointer to the associated account - returns an error if one occurs

func (*Store) FindArticleBySlug

func (s *Store) FindArticleBySlug(slug string) (*Article, error)

Find article by slug - accepts a string of the articles slug - returns pointer to the article

func (*Store) FindAssetByID

func (s *Store) FindAssetByID(id primitive.ObjectID) (*Asset, error)

find asset (not source) by it's id

func (*Store) FindAssetSourceByHash

func (s *Store) FindAssetSourceByHash(hash string) (*AssetSource, error)

find asset source by it's hash (sha256)

func (*Store) FindAssetsBySourceIDAccountID

func (s *Store) FindAssetsBySourceIDAccountID(source_id, account_id primitive.ObjectID) ([]*Asset, error)

find all assets with given source id and account id

func (*Store) FindBoardByObjectID

func (s *Store) FindBoardByObjectID(id primitive.ObjectID) (*Board, error)

Find board by _id - accepts primitive.ObjectID of the board (_id) - returns a pointer to the board

func (*Store) FindBoardByShort

func (s *Store) FindBoardByShort(short string) (*Board, error)

Find board by short - accepts a string of the board short name - returns a pointer to the board

func (*Store) FindSession

func (s *Store) FindSession(id string) (*Session, error)

Find Session - accepts a string of the session id - returns a pointer to the session - returns an error if one occurs

func (*Store) FindThreadBySlug

func (s *Store) FindThreadBySlug(slug string) (*Thread, error)

Find thread by slug - accepts a string of thread slug - returns a pointer to the thread

func (*Store) HydrateCache

func (s *Store) HydrateCache() error

Hydrate Cache - returns an error if one occurs

func (*Store) ResolveIdentity

func (s *Store) ResolveIdentity(account_id, thread_id primitive.ObjectID) (*Identity, error)

Resolves an identity from a particular account & thread if an identity cannot be found, one will be created and saved - accepts primitive.ObjectID's of the account and thread - returns a pointer to the identity

func (*Store) RunAggregation

func (s *Store) RunAggregation(col string, pipe any) ([]bson.M, error)

Run Aggregation - accepts a string of the collection name - accepts a (usually binary object notation) pipeline to be ran - returns a slice of bson.M containing the results - returns an error if one occurs

func (*Store) SaveNewMulti

func (s *Store) SaveNewMulti(documents []any, col string) error

Save Multiple Documents - accepts a slice of (usually binary object notations) documents but could be other types - accepts a string of the collection name - returns an error if one occurs

func (*Store) SaveNewSingle

func (s *Store) SaveNewSingle(document any, col string) error

Save a Single Document - accepts a (usually binary object notation) document but could be other types - accepts a string of the collection name - returns an error if one occurs

func (*Store) UpdateArticle

func (s *Store) UpdateArticle(article *Article) error

Update the provided article uses the provided article's ID to determine which to update - accepts a pointer to an article - returns an error if one occurred, else nil

func (*Store) UpdateBoard

func (s *Store) UpdateBoard(board *Board) error

Update the provided board uses the passed board's ID and short name to determine which to update. essentially replaces old with new. - accepts a pointer to a Board object - returns an error if one occurred, else nil

func (*Store) UpdateSession

func (s *Store) UpdateSession(session *Session) error

Update the provided session uses provided session's ID to determine which to update - accepts a pointer to the session - returns an error if one occurred, else nil

func (*Store) UpdateThread

func (s *Store) UpdateThread(thread *Thread) error

Update the provided thread uses the passed thread's ID and slug to determine which to update, essentially replaces old with new. - accepts a pointer to a Thread object - returns an error if one occurred, else nil

type TemplateStore

type TemplateStore struct {
	// html templates replace all html character codes, our case is iterative which means we must use text templates
	// for any html we wish to inject because our previous iterations become invalid.
	HtmlReplTempl *template.Template
	Text          map[string]*texttempl.Template
	PostLinkKinds []PostLink
}

func NewTemplateStore

func NewTemplateStore() *TemplateStore

func (*TemplateStore) Hydrate

func (ts *TemplateStore) Hydrate()

func (*TemplateStore) NormalizeCharCodes

func (ts *TemplateStore) NormalizeCharCodes(text string) string

normalizes character codes for parsing, as copy/pasted texts that seem identical are not always.

func (*TemplateStore) NormalizeLineEndings

func (ts *TemplateStore) NormalizeLineEndings(text string) string

normalizes line endings between windows/mac to all use linux LF style endings

func (*TemplateStore) Parse

func (ts *TemplateStore) Parse(text string) (string, error)

parses user content to generate an html output

func (ts *TemplateStore) ParsePostLinks(text string) (string, error)

parses entire input's post links, all instances will be replaced

func (*TemplateStore) ReplaceChars

func (ts *TemplateStore) ReplaceChars(text string) (string, error)

uses go's template system to sanitize html into their character codes utf-8 (js uses utf-16) raw text should already be sanitized for destructive content earlier

func (*TemplateStore) WrapContent

func (ts *TemplateStore) WrapContent(text string) (string, error)

wraps content in a container div with the content-body css class. this occurs for all types of submitted content as article body text

func (*TemplateStore) WrapParagraphs

func (ts *TemplateStore) WrapParagraphs(text string) (string, error)

parses out excessive new lines & whitespace and deliminates lines into paragraph tags any line without text (line is only newline char) is the space between paragraphs, which is why we parse out extra nonsesne here to make it easier. also normalizes line endings to LF style endings

func (*TemplateStore) WrapQuotes

func (ts *TemplateStore) WrapQuotes(text string) (string, error)

type Thread

type Thread struct {
	ID     primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
	Status ThreadStatus       `bson:"status" json:"status"`

	Title string `bson:"title" json:"title"`
	Body  string `bson:"body" json:"body"`
	Slug  string `bson:"slug" json:"slug"`

	Board primitive.ObjectID `bson:"board" json:"board"`

	// Creator is the Identity made for the creator of the thread, not the account id
	Creator primitive.ObjectID `bson:"creator" json:"creator"`

	Posts []primitive.ObjectID `bson:"posts" json:"posts"`
	Mods  []primitive.ObjectID `bson:"mods" json:"mods"`

	Assets []primitive.ObjectID `bson:"assets" json:"assets"`

	Tags  []string     `bson:"tags" json:"tags"`
	Flags []ThreadFlag `bson:"flags" json:"flags"`

	CreatedAt *time.Time `bson:"created_at" json:"created_at"`
	UpdatedAt *time.Time `bson:"updated_at" json:"updated_at"`
	DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}

func NewThread

func NewThread() *Thread

func (*Thread) AttachFlags

func (t *Thread) AttachFlags(rft *RUMThreadFlags)

for now use strings - eventually use bitfields for performance

func (*Thread) CLFormat

func (t *Thread) CLFormat() bson.M

ClientFormatter implementation

func (*Thread) HasFlag

func (t *Thread) HasFlag(flag ThreadFlag) bool

func (*Thread) Validate

func (t *Thread) Validate() error

type ThreadFlag

type ThreadFlag string

user settable flags

const (
	TF_NSFW     ThreadFlag = "nsfw"
	TF_NSFL     ThreadFlag = "nsfl"
	TF_MEDIAREQ ThreadFlag = "media_required"
)

type ThreadRole

type ThreadRole string
const (
	ThreadRoleUser    ThreadRole = "user"
	ThreadRoleMod     ThreadRole = "mod"
	ThreadRoleCreator ThreadRole = "creator"
)

type ThreadStatus

type ThreadStatus string
const (
	ThreadStatusOpen     ThreadStatus = "open"
	ThreadStatusClosed   ThreadStatus = "closed"
	ThreadStatusArchived ThreadStatus = "archived"
	ThreadStatusDeleted  ThreadStatus = "deleted"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL