Documentation ¶
Index ¶
- Constants
- Variables
- func FindFilterIdentityInThread(thread_id, account_id primitive.ObjectID) bson.D
- func GetFileChecksumMD5(filename string) (string, error)
- func GetFileChecksumSHA256(filename string) (string, error)
- func GetFileSize(filename string) (int64, error)
- func NewArticleSlug() string
- func NewFileName() (string, error)
- func NewIdentityStyle() string
- func NewThreadSlug() string
- func RequestLogger(rc *RequestCtx)
- func UploadFileToSpaces(tasset *utils.TempAsset) (string, error)
- type APIError
- type APIResource
- type Account
- type AccountCtx
- type AccountRole
- type AccountStatus
- type Article
- type ArticleAuthor
- type ArticleComment
- type ArticleStatus
- type Asset
- type AssetSource
- type AssetSourceDetails
- type AssetType
- type Board
- type ClientFormatter
- type FileCtx
- type FileUploadDetails
- type HashMethod
- type Identity
- type IdentityStatus
- type PageCtx
- type Post
- type PostLink
- type QueryCtx
- type RUMAssetAttachment
- type RUMComment
- type RUMPost
- type RUMSession
- type RUMThread
- type RUMThreadFlags
- type RequestCtx
- type RequestUnmarshaller
- type RoutingHandler
- type ServerCache
- type ServerError
- type Session
- type Store
- func (s *Store) AssetHashCollisionResolver(queries ...primitive.D) (*AssetSource, error)
- func (s *Store) CountResults(col string, filter bson.D) int64
- func (s *Store) DeleteSingle(id primitive.ObjectID, col string) error
- func (s *Store) FindAccountByUsernameOrEmail(username string, email string) (*Account, error)
- func (s *Store) FindAccountFromSession(id string) (*Account, error)
- func (s *Store) FindArticleBySlug(slug string) (*Article, error)
- func (s *Store) FindAssetByID(id primitive.ObjectID) (*Asset, error)
- func (s *Store) FindAssetSourceByHash(hash string) (*AssetSource, error)
- func (s *Store) FindAssetsBySourceIDAccountID(source_id, account_id primitive.ObjectID) ([]*Asset, error)
- func (s *Store) FindBoardByObjectID(id primitive.ObjectID) (*Board, error)
- func (s *Store) FindBoardByShort(short string) (*Board, error)
- func (s *Store) FindSession(id string) (*Session, error)
- func (s *Store) FindThreadBySlug(slug string) (*Thread, error)
- func (s *Store) HydrateCache() error
- func (s *Store) ResolveIdentity(account_id, thread_id primitive.ObjectID) (*Identity, error)
- func (s *Store) RunAggregation(col string, pipe any) ([]bson.M, error)
- func (s *Store) SaveNewMulti(documents []any, col string) error
- func (s *Store) SaveNewSingle(document any, col string) error
- func (s *Store) UpdateArticle(article *Article) error
- func (s *Store) UpdateBoard(board *Board) error
- func (s *Store) UpdateSession(session *Session) error
- func (s *Store) UpdateThread(thread *Thread) error
- type TemplateStore
- func (ts *TemplateStore) Hydrate()
- func (ts *TemplateStore) NormalizeCharCodes(text string) string
- func (ts *TemplateStore) NormalizeLineEndings(text string) string
- func (ts *TemplateStore) Parse(text string) (string, error)
- func (ts *TemplateStore) ParsePostLinks(text string) (string, error)
- func (ts *TemplateStore) ReplaceChars(text string) (string, error)
- func (ts *TemplateStore) WrapContent(text string) (string, error)
- func (ts *TemplateStore) WrapParagraphs(text string) (string, error)
- func (ts *TemplateStore) WrapQuotes(text string) (string, error)
- type Thread
- type ThreadFlag
- type ThreadRole
- type ThreadStatus
Constants ¶
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 ¶
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 )
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"} )
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"} )
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"} )
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:]]+`) )
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 )
var ( // permissions PUBLIC_BOARD_FIELDS = []string{"title", "short", "description", "threads", "created_at", "updated_at", "deleted_at"} )
var ServerErrorStatusMap map[int]ServerError = map[int]ServerError{ http.StatusInternalServerError: Error_Unexpected, http.StatusNotFound: Error_NotFound, http.StatusBadRequest: Error_Invalid, http.StatusUnauthorized: Error_Unauthorized, }
Status codes mapped to their respective ServerError
Functions ¶
func FindFilterIdentityInThread ¶
returns a filter object that resolves an identity from a particular thread using specified account
func GetFileChecksumMD5 ¶
returns an md5 checksum of the file located at filename
func GetFileChecksumSHA256 ¶
returns an sha256 checksum of the file located at filename
func GetFileSize ¶
returns the size of the file located at filename
func NewArticleSlug ¶
func NewArticleSlug() string
func NewFileName ¶
func NewIdentityStyle ¶
func NewIdentityStyle() string
func NewThreadSlug ¶
func NewThreadSlug() string
Types ¶
type APIError ¶
This should be the ONLY error type a client receives. Ever. Read top of file for more info.
func ErrorInvalid ¶
New Invalid Error - accepts a string of what was invalid
func ErrorNotFound ¶
New Not Found Error - accepts a string of the resource that was not found
func NewAPIError ¶
Creates a new APIError with the given status and message
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
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
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 ¶
clone an existing asset but replace the account_id with a new one
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 AssetType ¶
type AssetType string
available asset types that can be uploaded - open for expansion
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"` }
type ClientFormatter ¶
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.
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
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
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"` }
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
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 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 ¶
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_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 ¶
creates a new session for the given account
func (*Session) IsExpiringSoon ¶
will the session expire within the next hour?
func (*Session) IsExpiryImminent ¶
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 (*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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
Find article by slug - accepts a string of the articles slug - returns pointer to the article
func (*Store) FindAssetByID ¶
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 ¶
Find board by _id - accepts primitive.ObjectID of the board (_id) - returns a pointer to the board
func (*Store) FindBoardByShort ¶
Find board by short - accepts a string of the board short name - returns a pointer to the board
func (*Store) FindSession ¶
Find Session - accepts a string of the session id - returns a pointer to the session - returns an error if one occurs
func (*Store) FindThreadBySlug ¶
Find thread by slug - accepts a string of thread slug - returns a pointer to the thread
func (*Store) HydrateCache ¶
Hydrate Cache - returns an error if one occurs
func (*Store) ResolveIdentity ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 (*TemplateStore) ParsePostLinks ¶
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 (*Thread) AttachFlags ¶
func (t *Thread) AttachFlags(rft *RUMThreadFlags)
for now use strings - eventually use bitfields for performance
func (*Thread) HasFlag ¶
func (t *Thread) HasFlag(flag ThreadFlag) bool
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" )