notebrew

package module
v0.0.16 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2024 License: MIT Imports: 82 Imported by: 1

README

Notebrew

Notebrew is a self-hostable static site CMS in a single binary.

Installation

  1. Install Git.

  2. Install Go.

  3. go install -tags fts5 github.com/bokwoon95/notebrew/notebrew@latest
    
  4. notebrew # or "$(go env GOPATH)/bin/notebrew" if you have not added $GOPATH/bin into your $PATH.
    

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (

	// RuntimeFS is the FS containing the runtime files needed by notebrew for
	// operation.
	RuntimeFS fs.FS = embedFS

	// StylesCSS is the contents of the styles.css file in the embed/
	// directory.
	StylesCSS string

	// StylesCSSHash is the sha256 hash of the StylesCSS contents.
	StylesCSSHash string

	// BaselineJS is the contents of the baseline.js file in the embed/
	// directory.
	BaselineJS string

	// BaselineJSHash is the sha256 hash of the BaselineJS contents.
	BaselineJSHash string

	// Version holds the current notebrew git revision.
	Version string

	// CountryCodes is the ISO code to country mapping from country_codes.json
	// in the embed/ directory.
	CountryCodes map[string]string

	// ReservedSubdomains is the list of reserved subdomains that users will
	// not be able to use on the content domain.
	ReservedSubdomains = []string{"www", "cdn", "storage", "videocdn", "videostorage"}
)
View Source
var AllowedFileTypes = map[string]FileType{
	".html": {
		Ext:         ".html",
		ContentType: "text/html; charset=utf-8",
		SizeLimit:   1 << 20,
		Attribute:   AttributeGzippable | AttributeEditable,
	},
	".css": {
		Ext:         ".css",
		ContentType: "text/css; charset=utf-8",
		SizeLimit:   1 << 20,
		Attribute:   AttributeGzippable | AttributeEditable,
	},
	".js": {
		Ext:         ".js",
		ContentType: "text/javascript; charset=utf-8",
		SizeLimit:   1 << 20,
		Attribute:   AttributeGzippable | AttributeEditable,
	},
	".md": {
		Ext:         ".md",
		ContentType: "text/markdown; charset=utf-8",
		SizeLimit:   1 << 20,
		Attribute:   AttributeGzippable | AttributeEditable,
	},
	".txt": {
		Ext:         ".txt",
		ContentType: "text/plain; charset=utf-8",
		SizeLimit:   1 << 20,
		Attribute:   AttributeGzippable | AttributeEditable,
	},
	".jpeg": {
		Ext:         ".jpeg",
		ContentType: "image/jpeg",
		SizeLimit:   10 << 20,
		Attribute:   AttributeObject | AttributeImg,
	},
	".jpg": {
		Ext:         ".jpg",
		ContentType: "image/jpeg",
		SizeLimit:   10 << 20,
		Attribute:   AttributeObject | AttributeImg,
	},
	".png": {
		Ext:         ".png",
		ContentType: "image/png",
		SizeLimit:   10 << 20,
		Attribute:   AttributeObject | AttributeImg,
	},
	".webp": {
		Ext:         ".webp",
		ContentType: "image/webp",
		SizeLimit:   10 << 20,
		Attribute:   AttributeObject | AttributeImg,
	},
	".gif": {
		Ext:         ".gif",
		ContentType: "image/gif",
		SizeLimit:   10 << 20,
		Attribute:   AttributeObject | AttributeImg,
	},
	".svg": {
		Ext:         ".svg",
		ContentType: "image/svg+xml",
		SizeLimit:   1 << 20,
		Attribute:   AttributeGzippable | AttributeImg,
	},
	".eot": {
		Ext:         ".eot",
		ContentType: "font/eot",
		SizeLimit:   2 << 20,
		Attribute:   AttributeGzippable | AttributeFont,
	},
	".otf": {
		Ext:         ".otf",
		ContentType: "font/otf",
		SizeLimit:   2 << 20,
		Attribute:   AttributeGzippable | AttributeFont,
	},
	".ttf": {
		Ext:         ".ttf",
		ContentType: "font/ttf",
		SizeLimit:   2 << 20,
		Attribute:   AttributeGzippable | AttributeFont,
	},
	".woff": {
		Ext:         ".woff",
		ContentType: "font/woff",
		SizeLimit:   2 << 20,
		Attribute:   AttributeFont,
	},
	".woff2": {
		Ext:         ".woff2",
		ContentType: "font/woff2",
		SizeLimit:   2 << 20,
		Attribute:   AttributeFont,
	},
	".atom": {
		Ext:         ".atom",
		ContentType: "application/atom+xml; charset=utf-8",
		SizeLimit:   1 << 20,
		Attribute:   AttributeGzippable,
	},
	".json": {
		Ext:         ".json",
		ContentType: "application/json",
		SizeLimit:   1 << 20,
		Attribute:   AttributeGzippable,
	},
	".tgz": {
		Ext:         ".tgz",
		ContentType: "application/octet-stream",
		SizeLimit:   -1,
		Attribute:   AttributeObject | AttributeAttachment,
	},
}

AllowedFileTypes is a list of file types allowed by notebrew.

It is exported so that people using notebrew as a library may add their own file types to it, although no effort has been made to test whether such additions would work seamlessly with the rest of the library.

View Source
var ErrStorageLimitExceeded = fmt.Errorf("storage limit exceeded")

ErrStorageLimitExceeded is the error returned by an operation if a user exceeded their storage limit during the operation.

View Source
var LoggerKey = &contextKey{}

LoggerKey is the key used by notebrew for setting and getting a logger from the request context.

Functions

func CreationTime

func CreationTime(absolutePath string, fileInfo fs.FileInfo) time.Time

CreationTime returns the creation time of a file given its absolute file path and fs.FileInfo.

func DatabaseCatalog

func DatabaseCatalog(dialect string) (*ddl.Catalog, error)

DatabaseCatalog returns a catalog representing the desired catalog schema for the database.

func FilesCatalog

func FilesCatalog(dialect string) (*ddl.Catalog, error)

FilesCatalog returns a catalog representing the desired catalog schema for the files database.

func HumanReadableFileSize

func HumanReadableFileSize(size int64) string

HumanReadableFileSize returns a human readable file size of an int64 size in bytes.

func IsForeignKeyViolation

func IsForeignKeyViolation(dialect string, errorCode string) bool

IsForeignKeyViolation returns true if the provided errorCode matches the dialect-specific code for representing a foreign key constraint violation.

func IsFulltextIndexed

func IsFulltextIndexed(filePath string) bool

IsFulltextIndexed reports whether a file is eligible for fulltext indexing by virtue of its position within the filesystem. For example, html files in the pages directory are fulltext indexed but not html files in the output directory because those are generated by notebrew itself (we don't want to index generated files, only user source files).

If a file is not fulltext-indexed, it is stored in a DatabaseFS in gzipped form to save space and also to save CPU cycles as we don't have to gzip again when serving it later.

func IsKeyViolation

func IsKeyViolation(dialect string, errorCode string) bool

IsKeyViolation returns true if the provided errorCode matches the dialect-specific code for representing a primary key/unique constraint violation.

func NewTemplateError

func NewTemplateError(err error) error

NewTemplateError constructs a new TemplateError from an error returned by the html/template package by parsing the error string, which is unfortunately the only way we can extract the template name and line number.

func RealClientIP

func RealClientIP(r *http.Request, realIPHeaders map[netip.Addr]string, proxyIPs map[netip.Addr]struct{}) netip.Addr

RealClientIP returns the real client IP of the request.

func ServeFile

func ServeFile(w http.ResponseWriter, r *http.Request, name string, size int64, fileType FileType, reader io.Reader, cacheControl string)

ServeFile serves a given file (in the form of an io.Reader). It applies a potential list of optimizations such as gzipping the response, handling Range requests, calculating the ETag and setting the Cache-Control header.

func UnmarshalCatalog

func UnmarshalCatalog(dialect string, b []byte) (*ddl.Catalog, error)

UnmarshalCatalog unmarshals a JSON payload into a *ddl.Catalog.

Types

type AtomCDATA

type AtomCDATA struct {
	Type    string `xml:"type,attr"`
	Content string `xml:",cdata"`
}

AtomCDATA represents CDATA in atom.

type AtomEntry

type AtomEntry struct {
	ID        string     `xml:"id"`
	Title     string     `xml:"title"`
	Published string     `xml:"published"`
	Updated   string     `xml:"updated"`
	Link      []AtomLink `xml:"link"`
	Summary   AtomText   `xml:"summary"`
	Content   AtomCDATA  `xml:"content"`
}

AtomEntry represents an atom entry (i.e. a post).

type AtomFeed

type AtomFeed struct {
	XMLName xml.Name    `xml:"feed"`
	Xmlns   string      `xml:"xmlns,attr"`
	ID      string      `xml:"id"`
	Title   string      `xml:"title"`
	Updated string      `xml:"updated"`
	Link    []AtomLink  `xml:"link"`
	Entry   []AtomEntry `xml:"entry"`
}

AtomFeed represents an atom feed (i.e. a list of posts).

type AtomLink struct {
	Href string `xml:"href,attr"`
	Rel  string `xml:"rel,attr"`
}

AtomLink represents an atom link.

type AtomText

type AtomText struct {
	Type    string `xml:"type,attr"`
	Content string `xml:",chardata"`
}

AtomText represents some text in atom.

type Attribute

type Attribute int

Attribute represents the various attributes that a file type have e.g. whether it is gzippable, whether it's an object, etc.

const (
	AttributeGzippable  Attribute = 1 << 0 // Should be gzipped when sent in the response body.
	AttributeObject     Attribute = 1 << 1 // Should be stored in ObjectStorage (instead of the database).
	AttributeAttachment Attribute = 1 << 2 // Should be downloaded with Content-Disposition: attachment.
	AttributeEditable   Attribute = 1 << 3 // Can be edited by the user.
	AttributeFont       Attribute = 1 << 4 // Can be used as a @font-face src.
	AttributeImg        Attribute = 1 << 5 // Can be displayed with an <img> tag.
	AttributeVideo      Attribute = 1 << 6 // Can be displayed with a <video> tag.
)

type DatabaseFS

type DatabaseFS struct {
	// DB is the sql database.
	DB *sql.DB

	// Dialect is the database dialect. Currently, the only dialects supported
	// are "sqlite", "postgres" and "mysql".
	Dialect string

	// ErrorCode converts an error returned by a database query into a
	// dialect-specific error code.
	ErrorCode func(error) string

	// ObjectStorage is used for storage of binary objects.
	ObjectStorage ObjectStorage

	// Logger is used for reporting errors that cannot be handled and are
	// thrown away.
	Logger *slog.Logger

	// UpdateStorageUsed is called whenever the storage used by a site changes
	// (delta being the number of bytes that have been added or removed).
	// Specifically, this happens on calls to OpenWriter, Remove, RemoveAll and
	// Copy.
	UpdateStorageUsed func(ctx context.Context, siteName string, delta int64) error
	// contains filtered or unexported fields
}

DatabaseFS implements a writeable filesystem using a database and an ObjectStorage provider.

func NewDatabaseFS

func NewDatabaseFS(config DatabaseFSConfig) (*DatabaseFS, error)

NewDatabaseFS constructs a new DatabaseFS.

func (*DatabaseFS) As

func (fsys *DatabaseFS) As(target any) bool

As writes the current databaseFS into the target if it is a valid DatabaseFS pointer.

func (*DatabaseFS) Copy

func (fsys *DatabaseFS) Copy(srcName, destName string) error

Copy implements the Copy FS operation for DatabaseFS.

func (*DatabaseFS) Mkdir

func (fsys *DatabaseFS) Mkdir(name string, _ fs.FileMode) error

Mkdir implements the Mkdir FS operation for DatabaseFS.

func (*DatabaseFS) MkdirAll

func (fsys *DatabaseFS) MkdirAll(name string, _ fs.FileMode) error

MkdirAll implements the MkdirAll FS operation for DatabaseFS.

func (*DatabaseFS) Open

func (fsys *DatabaseFS) Open(name string) (fs.File, error)

Open implements the Open FS operation for DatabaseFS.

func (*DatabaseFS) OpenWriter

func (fsys *DatabaseFS) OpenWriter(name string, _ fs.FileMode) (io.WriteCloser, error)

OpenWriter implements the OpenWriter FS operation for DatabaseFS.

func (*DatabaseFS) ReadDir

func (fsys *DatabaseFS) ReadDir(name string) ([]fs.DirEntry, error)

ReadDir implements the ReadDir FS operation for DatabaseFS.

func (*DatabaseFS) Remove

func (fsys *DatabaseFS) Remove(name string) error

Remove implements the Remove FS operation for DatabaseFS.

func (*DatabaseFS) RemoveAll

func (fsys *DatabaseFS) RemoveAll(name string) error

RemoveAll implements the RemoveAll FS operation for DatabaseFS.

func (*DatabaseFS) Rename

func (fsys *DatabaseFS) Rename(oldName, newName string) error

Rename implements the Rename FS operation for DatabaseFS.

func (*DatabaseFS) Stat

func (fsys *DatabaseFS) Stat(name string) (fs.FileInfo, error)

Stat implements the fs.StatFS interface.

func (*DatabaseFS) WithContext

func (fsys *DatabaseFS) WithContext(ctx context.Context) FS

WithContext returns a new FS with the given context.

func (*DatabaseFS) WithValues

func (fsys *DatabaseFS) WithValues(values map[string]any) FS

WithValues returns a new FS with the given values.

Currently, the following values are recognized:

- "modTime" => time.Time (sets the modTime for files created by OpenWriter/Mkdir/MkdirAll)

- "creationTime" => time.Time (sets the creationTime for files created by OpenWriter/Mkdir/MkdirAll)

- "caption" => string (sets the caption for images created by OpenWriter)

These values will apply to *all* filesystem operations, so if you only want to set the modTime or creationTime for a specific file you will have to create a new instance of a DatabaseFS using WithValues(), create that file, then throw the DatabaseFS instance away.

type DatabaseFSConfig

type DatabaseFSConfig struct {
	// (Required) DB is the sql database.
	DB *sql.DB

	// (Required) Dialect is the database dialect. Currently, the only dialects
	// supported are "sqlite", "postgres" and "mysql".
	Dialect string

	// ErrorCode converts an error returned by a database query into a
	// dialect-specific error code.
	ErrorCode func(error) string

	// (Required) ObjectStorage is used for storage of binary objects.
	ObjectStorage ObjectStorage

	// (Required) Logger is used for reporting errors that cannot be handled
	// and are thrown away.
	Logger *slog.Logger

	// UpdateStorageUsed is called whenever the storage used by a site changes
	// (delta being the number of bytes that have been added or removed).
	// Specifically, this happens on calls to OpenWriter, Remove, RemoveAll and
	// Copy.
	UpdateStorageUsed func(ctx context.Context, siteName string, delta int64) error
}

DatabaseFSConfig holds the parameters needed to construct a DatabaseFS.

type DatabaseFile

type DatabaseFile struct {
	// contains filtered or unexported fields
}

DatabaseFile represents a readable instance of a file returned by DatabaseFS.

func (*DatabaseFile) Close

func (file *DatabaseFile) Close() error

Close closes the DatabaseFile from reading.

func (*DatabaseFile) Read

func (file *DatabaseFile) Read(p []byte) (n int, err error)

Read reads up to len(b) bytes from the DatabaseFile and stores them in b. It returns the number of bytes read and any error encountered. At end of file, Read returns 0, io.EOF.

func (*DatabaseFile) Stat

func (file *DatabaseFile) Stat() (fs.FileInfo, error)

Stat returns the file info describing the file.

type DatabaseFileInfo

type DatabaseFileInfo struct {
	FileID   ID
	FilePath string

	CreationTime time.Time
	// contains filtered or unexported fields
}

DatabaseFileInfo describes a file returned by DatabaseFS.

func (*DatabaseFileInfo) Info

func (fileInfo *DatabaseFileInfo) Info() (fs.FileInfo, error)

Returns the file info (needed to implement fs.DirEntry).

func (*DatabaseFileInfo) IsDir

func (fileInfo *DatabaseFileInfo) IsDir() bool

Whether the file is a directory.

func (*DatabaseFileInfo) ModTime

func (fileInfo *DatabaseFileInfo) ModTime() time.Time

Modification time.

func (*DatabaseFileInfo) Mode

func (fileInfo *DatabaseFileInfo) Mode() fs.FileMode

File mode bits.

func (*DatabaseFileInfo) Name

func (fileInfo *DatabaseFileInfo) Name() string

Base name of the file.

func (*DatabaseFileInfo) Size

func (fileInfo *DatabaseFileInfo) Size() int64

Size of the file in bytes.

func (*DatabaseFileInfo) Sys

func (fileInfo *DatabaseFileInfo) Sys() any

Sys always returns nil.

func (*DatabaseFileInfo) Type

func (fileInfo *DatabaseFileInfo) Type() fs.FileMode

Type bits (needed to implement fs.DirEntry).

type DatabaseFileWriter

type DatabaseFileWriter struct {
	// contains filtered or unexported fields
}

DatabaseFileWriter represents a writable file on a DatabaseFS.

func (*DatabaseFileWriter) Close

func (file *DatabaseFileWriter) Close() error

Close saves the contents of the DatabaseFileWriter into the database and closes the DatabaseFileWriter.

func (*DatabaseFileWriter) ReadFrom

func (file *DatabaseFileWriter) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom implements io.ReaderFrom.

func (*DatabaseFileWriter) Write

func (file *DatabaseFileWriter) Write(p []byte) (n int, err error)

Write writes len(b) bytes from b to the DatabaseFileWriter. It returns the number of bytes written and an error, if any. Write returns a non-nil error when n != len(b).

type DirectoryFS

type DirectoryFS struct {
	// RootDir is the root directory of the DirectoryFS. Has to be an absolute
	// path!!
	RootDir string

	// TempDir is used to store files while they are being written before being
	// renamed to their target destination when complete.
	//
	// Windows is the exception, as os.Rename on the temporary files (even
	// after I've closed them) intermittently returns "Access is denied" for no
	// discernable reason so I've skipped the temp file into rename workflow
	// for Windows entirely.
	TempDir string
	// contains filtered or unexported fields
}

DirectoryFS implements a writeable filesystem using a local directory.

NOTE: Each FS method should only ever work with forward slash file path separators. Even on Windows, because Windows accepts forward slash file path separators just fine. This simplifies our implementation a lot because we only ever have to think about forward slashes.

func NewDirectoryFS

func NewDirectoryFS(config DirectoryFSConfig) (*DirectoryFS, error)

NewDirectoryFS constructs a new DirectoryFS.

func (*DirectoryFS) As

func (fsys *DirectoryFS) As(target any) bool

As writes the current directoryFS into the target if it is a valid DirectoryFS pointer.

func (*DirectoryFS) Copy

func (fsys *DirectoryFS) Copy(srcName, destName string) error

Copy implements the Copy FS operation for DirectoryFS.

func (*DirectoryFS) Mkdir

func (fsys *DirectoryFS) Mkdir(name string, _ fs.FileMode) error

Mkdir implements the Mkdir FS operation for DirectoryFS.

func (*DirectoryFS) MkdirAll

func (fsys *DirectoryFS) MkdirAll(name string, _ fs.FileMode) error

MkdirAll implements the MkdirAll FS operation for DirectoryFS.

func (*DirectoryFS) Open

func (fsys *DirectoryFS) Open(name string) (fs.File, error)

Open implements the Open FS operation for DirectoryFS.

func (*DirectoryFS) OpenWriter

func (fsys *DirectoryFS) OpenWriter(name string, _ fs.FileMode) (io.WriteCloser, error)

OpenWriter implements the OpenWriter FS operation for DirectoryFS.

func (*DirectoryFS) ReadDir

func (fsys *DirectoryFS) ReadDir(name string) ([]fs.DirEntry, error)

ReadDir implements the ReadDir FS operation for DirectoryFS.

func (*DirectoryFS) Remove

func (fsys *DirectoryFS) Remove(name string) error

Remove implements the Remove FS operation for DirectoryFS.

func (*DirectoryFS) RemoveAll

func (fsys *DirectoryFS) RemoveAll(name string) error

RemoveAll implements the RemoveAll FS operation for DirectoryFS.

func (*DirectoryFS) Rename

func (fsys *DirectoryFS) Rename(oldName, newName string) error

Rename implements the Rename FS operation for DirectoryFS.

func (*DirectoryFS) Stat

func (fsys *DirectoryFS) Stat(name string) (fs.FileInfo, error)

Stat implements the fs.StatFS interface.

func (*DirectoryFS) WithContext

func (fsys *DirectoryFS) WithContext(ctx context.Context) FS

WithContext returns a new FS with the given context.

type DirectoryFSConfig

type DirectoryFSConfig struct {
	// Root directory of the DirectoryFS.
	RootDir string

	// Temp directory of the DirectoryFS.
	TempDir string
}

DirectoryFSConfig holds the parameters needed to construct a DirectoryFS.

type DirectoryFileWriter

type DirectoryFileWriter struct {
	// contains filtered or unexported fields
}

DirectoryFileWriter represents a writable file on a DirectoryFS.

func (*DirectoryFileWriter) Close

func (file *DirectoryFileWriter) Close() error

Close saves the contents of the DirectoryFileWriter into a file and closes the DirectoryFileWriter.

func (*DirectoryFileWriter) ReadFrom

func (file *DirectoryFileWriter) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom implements io.ReaderFrom.

func (*DirectoryFileWriter) Write

func (file *DirectoryFileWriter) Write(p []byte) (n int, err error)

Write writes len(b) bytes from b to the DirectoryFileWriter. It returns the number of bytes written and an error, if any. Write returns a non-nil error when n != len(b).

type DirectoryObjectStorage

type DirectoryObjectStorage struct {
	// Root directory to store objects in.
	RootDir string
}

DirectoryObjectStorage implements ObjectStorage using a local directory.

func NewDirObjectStorage

func NewDirObjectStorage(rootDir, tempDir string) (*DirectoryObjectStorage, error)

NewDirObjectStorage constructs a new DirectoryObjectStorage.

func (*DirectoryObjectStorage) Copy

func (storage *DirectoryObjectStorage) Copy(ctx context.Context, srcKey, destKey string) error

Copy implements the Copy ObjectStorage operation for DirectoryObjectStorage.

func (*DirectoryObjectStorage) Delete

func (storage *DirectoryObjectStorage) Delete(ctx context.Context, key string) error

Delete implements the Delete ObjectStorage operation for DirectoryObjectStorage.

func (*DirectoryObjectStorage) Get

func (storage *DirectoryObjectStorage) Get(ctx context.Context, key string) (io.ReadCloser, error)

Get implements the Get ObjectStorage operation for DirectoryObjectStorage.

func (*DirectoryObjectStorage) Put

func (storage *DirectoryObjectStorage) Put(ctx context.Context, key string, reader io.Reader) error

Put implements the Put ObjectStorage operation for DirectoryObjectStorage.

type FS

type FS interface {
	// WithContext returns a new FS with the given context which applies to all
	// subsequent operations carried out by the filesystem.
	WithContext(context.Context) FS

	// Open opens the named file.
	Open(name string) (fs.File, error)

	// OpenWriter opens an io.WriteCloser that represents a writeable instance
	// of a file. The parent directory must exist. If the file doesn't exist,
	// it should be created. If the file exists, its should be truncated. Write
	// operations should ideally be atomic i.e. if two writers are writing to
	// the same file, last writer wins.
	OpenWriter(name string, perm fs.FileMode) (io.WriteCloser, error)

	// ReadDir reads the named directory and returns a list of directory
	// entries sorted by filename.
	ReadDir(name string) ([]fs.DirEntry, error)

	// Mkdir creates a new directory with the specified name. The directory
	// should not already exist.
	Mkdir(name string, perm fs.FileMode) error

	// MkdirAll creates a directory with the given name, along with any
	// necessary parents, and returns nil, or else returns an error.
	MkdirAll(name string, perm fs.FileMode) error

	// Remove removes the named file or directory. The file should exist. If
	// the file is a directory, the directory should be empty.
	Remove(name string) error

	// RemoveAll removes name and any children it contains. It removes
	// everything it can but returns the first error it encounters. If the path
	// does not exist, RemoveAll returns nil (no error).
	RemoveAll(name string) error

	// Rename renames (moves) oldName to newName. newName must not exist.
	Rename(oldName, newName string) error

	// Copy copies the srcName to destName. destName must not exist.
	Copy(srcName, destName string) error
}

FS represents a writeable filesystem.

type FileType

type FileType struct {
	Ext         string    // File extension.
	ContentType string    // Content-Type that file should be served with.
	SizeLimit   int64     // Size limit for the file type.
	Attribute   Attribute // File type attributes.
}

FileType represents a file type.

func (FileType) Has

func (fileType FileType) Has(attribute Attribute) bool

Has reports whether a particular file type contains the attribute.

func (FileType) IsImg

func (fileType FileType) IsImg() bool

IsImg is shorthand for Has(AttributeImg). Used in templates, which do not have access to the Attribute* constants.

func (FileType) IsObject

func (fileType FileType) IsObject() bool

IsObject is shorthand for Has(AttributeObject). Used in templates, which do not have access to the Attribute* constants.

type Heading

type Heading struct {
	// ID of the heading.
	ID string

	// Title of the heading.
	Title string

	// Level of the heading i.e. for h1 - h6.
	Level int

	// Subheadings of the heading.
	Subheadings []Heading

	// Running count of the heading.
	Position int
}

Heading represents a markdown heading.

type ID

type ID [16]byte

ID is a 16-byte UUID that is sortable by a timestamp component. The first 5 bytes of the ID are the timestamp component and the remaining 11 bytes are completely random. Unlike UUIDv7, there is no version byte and there are no monotonic guarantees, which makes generating a spec-compliant ID much simpler.

The timestamp component is 5 bytes so that it can be perfectly encoded by an 8-character Base32 string. While this feature is not used by IDs because they are always displayed in canonical UUID form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx for maxmimum compatibility, these 5 byte timestamps appear in other places in notebrew which do exploit the fact that they can be perfectly encoded by an 8-character Base32 string so it just seems like a good idea to ensure all timestamp prefixes occupy 5 bytes for consistency.

func NewID

func NewID() ID

NewID creates a new ID.

func ParseID

func ParseID(s string) (ID, error)

ParseID parses a UUID string of the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx into an ID.

func (ID) IsZero

func (id ID) IsZero() bool

IsZero reports if an ID is all zeroes, which is used to represent the null state.

func (ID) MarshalJSON

func (id ID) MarshalJSON() ([]byte, error)

MarshalJSON converts an ID to a JSON string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.

func (ID) String

func (id ID) String() string

String prints the ID in UUID format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.

func (*ID) UnmarshalJSON

func (id *ID) UnmarshalJSON(data []byte) error

UnmarshalJSON converts a JSON string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx into an ID. If the JSON value is null, the existing ID's value is untouched.

type Image

type Image struct {
	// Parent URL of the image.
	Parent string

	// Name of the image.
	Name string

	// AltText of the image.
	AltText string

	// Caption of the image.
	Caption string
}

Image contains metadata about an image.

type LimitedWriter

type LimitedWriter struct {
	W   io.Writer // underlying writer
	N   int64     // max bytes remaining
	Err error     // error to be returned once limit is reached
}

LimitedWriter is the missing counterpart of LimitedReader from the stdlib. https://github.com/golang/go/issues/54111#issuecomment-1220793565

func (*LimitedWriter) Write

func (lw *LimitedWriter) Write(p []byte) (int, error)

Write implements io.Writer.

type Mail

type Mail struct {
	// MailFrom is the SMTP MAIL FROM instruction.
	MailFrom string

	// RcptTo is the SMTP RCPT TO instruction.
	RcptTo string

	// Headers are the SMTP headers, in the header name value pairs. Example:
	//
	//   []string{
	//       "Subject", "Hello World!",
	//       "Content-Type", "text/html; charset=utf-8",
	//       "Reply-To", "helpdesk@example.com",
	//   }
	Headers []string

	// Body is the SMTP email body.
	Body io.Reader
}

Mail represents an SMTP email to be sent.

type Mailer

type Mailer struct {
	// (Required) SMTP username.
	Username string

	// (Required) SMTP password.
	Password string

	// (Required) SMTP host.
	Host string

	// (Required) SMTP port.
	Port string

	// Limiter is a rate limiter set by the LimitInterval and LimitBurst fields
	// in MailerConfig.
	Limiter *rate.Limiter

	// C is the mail channel.
	C chan Mail

	// Logger is used for reporting errors that cannot be handled and are
	// thrown away.
	Logger *slog.Logger
	// contains filtered or unexported fields
}

Mailer is responsible for sending emails (using SMTP). Send emails by writing to its C channel. Example:

mailer.C <- Mail{
    MailFrom: "sender@email.com",
    RcptTo:   "recipient@example.com",
    Headers: []string{
        "Subject", "Hello World!",
        "Content-Type", "text/html; charset=utf-8",
    },
    Body: strings.NewReader("<h1>Hello World!</h1>"),
}

Once the mailer sends an email, it will keep the SMTP connection alive for up to 100 seconds. If another mail comes through the channel during that period, the SMTP connection will be reused to send that email and refreshes its timeout duration for another 100 seconds. When the mailer times out, the SMTP connection will be closed and any future mails will require establishing a new connection to the SMTP server.

Using rate limiter is opt-in (but advised) and has to be manually invoked by calling mailer.Limiter.Wait(ctx) or mailer.Limiter.Allow().

func NewMailer

func NewMailer(config MailerConfig) (*Mailer, error)

NewMailer constructs a new Mailer. It starts a background job listening for incoming mails on the C channel, which can be shut down by calling the Close() method.

func (*Mailer) Close

func (mailer *Mailer) Close() error

Close shuts down the background job listening for incoming mail on the C channel and returns when the background job is completed.

func (*Mailer) NewClient

func (mailer *Mailer) NewClient() (*smtp.Client, error)

NewClient returns a new SMTP client.

type MailerConfig

type MailerConfig struct {
	// (Required) SMTP username.
	Username string

	// (Required) SMTP password.
	Password string

	// (Required) SMTP host.
	Host string

	// (Required) SMTP port.
	Port string

	// (Required) Interval for replenishing one token back to the rate limiter bucket.
	LimitInterval time.Duration

	// (Required) Maximum tokens that can be held by the rate limiter bucket at any time.
	LimitBurst int

	// (Required) Logger is used for reporting errors that cannot be handled
	// and are thrown away.
	Logger *slog.Logger
}

MailerConfig holds the parameters needed to construct a Mailer.

type MaxMindDBRecord

type MaxMindDBRecord struct {
	Country struct {
		ISOCode string `maxminddb:"iso_code"`
	} `maxminddb:"country"`
}

MaxMindDBRecord is the struct used to retrieve the country for an IP address.

type NavigationLink struct {
	// Name of the link.
	Name string

	// URL of the link.
	URL string
}

NavigationLink represents a navigation link to be displayed in the site's navigation menu.

type Notebrew

type Notebrew struct {
	// FS is the file system associated with the notebrew instance.
	FS FS

	// DB is the DB associated with the notebrew instance.
	DB *sql.DB

	// Dialect is Dialect of the database. Only sqlite, postgres and mysql
	// databases are supported.
	Dialect string

	// ErrorCode translates a database error into an dialect-specific error
	// code. If the error is not a database error or if no underlying
	// implementation is provided, ErrorCode should return an empty string.
	ErrorCode func(error) string

	// CMSDomain is the domain that the notebrew is using to serve the CMS.
	// Examples: localhost:6444, notebrew.com
	CMSDomain string

	// CMSDomainHTTPS indicates whether the CMS domain is currently being
	// served over HTTPS.
	CMSDomainHTTPS bool

	// ContentDomain is the domain that the notebrew instance is using to serve
	// the static generated content. Examples: localhost:6444, nbrew.net.
	ContentDomain string

	// ContentDomainHTTPS indicates whether the content domain is currently
	// being served over HTTPS.
	ContentDomainHTTPS bool

	// CDNDomain is the domain of the CDN that notebrew is using to host its
	// images. Examples: cdn.nbrew.net, nbrewcdn.net.
	CDNDomain string

	// ImgCmd is the command (must reside in $PATH) used to preprocess images
	// for the web before they are saved to the FS. Images in the notes folder
	// are never prerpocessed and are uploaded as-is. This serves as an a
	// escape hatch for users who wish to upload their images without any image
	// preprocessing, as they can upload images to the notes folder first
	// before moving it elsewhere.
	//
	// ImgCmd should take in arguments in the form of `<ImgCmd> $INPUT_PATH
	// $OUTPUT_PATH`, where $INPUT_PATH is the input path to the raw image and
	// $OUTPUT_PATH is output path where ImgCmd should save the preprocessed
	// image.
	ImgCmd string

	// (Required) Port is port that notebrew is listening on.
	Port int

	// IP4 is the IPv4 address of the current machine, if notebrew is currently
	// serving either port 80 (HTTP) or 443 (HTTPS).
	IP4 netip.Addr

	// IP6 is the IPv6 address of the current machine, if notebrew is currently
	// serving either port 80 (HTTP) or 443 (HTTPS).
	IP6 netip.Addr

	// Domains is the list of domains that need to point at notebrew for it to
	// work. Does not include user-created domains.
	Domains []string

	// ManagingDomains is the list of domains that the current instance of
	// notebrew is managing SSL certificates for.
	ManagingDomains []string

	// Captcha configuration.
	CaptchaConfig struct {
		// Captcha widget's script src. e.g. https://js.hcaptcha.com/1/api.js,
		// https://challenges.cloudflare.com/turnstile/v0/api.js
		WidgetScriptSrc template.URL

		// Captcha widget's container div class. e.g. h-captcha, cf-turnstile
		WidgetClass string

		// Captcha verification URL to make POST requests to. e.g.
		// https://api.hcaptcha.com/siteverify,
		// https://challenges.cloudflare.com/turnstile/v0/siteverify
		VerificationURL string

		// Captcha response token name. e.g. h-captcha-response,
		// cf-turnstile-response
		ResponseTokenName string

		// Captcha site key.
		SiteKey string

		// Captcha secret key.
		SecretKey string

		// CSP contains the Content-Security-Policy directive names and values
		// required for the captcha widget to work.
		CSP map[string]string
	}

	// Mailer is used to send out transactional emails e.g. password reset
	// emails.
	Mailer *Mailer

	// The default value for the SMTP MAIL FROM instruction.
	MailFrom string

	// The default value for the SMTP Reply-To header.
	ReplyTo string

	// Proxy configuration.
	ProxyConfig struct {
		// RealIPHeaders contains trusted IP addresses to HTTP headers that
		// they are known to populate the real client IP with. e.g. X-Real-IP,
		// True-Client-IP.
		RealIPHeaders map[netip.Addr]string

		// Contains the set of trusted proxy IP addresses. This is used when
		// resolving the real client IP from the X-Forwarded-For HTTP header
		// chain from right (most trusted) to left (most accurate).
		ProxyIPs map[netip.Addr]struct{}
	}

	// DNS provider (required for using wildcard certificates with
	// LetsEncrypt).
	DNSProvider interface {
		libdns.RecordAppender
		libdns.RecordDeleter
		libdns.RecordGetter
		libdns.RecordSetter
	}

	// CertStorage is the magic (certmagic) that automatically provisions SSL
	// certificates for notebrew.
	CertStorage certmagic.Storage

	// CertLogger is the logger used for a certmagic.Config.
	CertLogger *zap.Logger

	// ContentSecurityPolicy is the Content-Security-Policy HTTP header set for
	// every HTML response served on the CMS domain.
	ContentSecurityPolicy string

	// Logger is used for reporting errors that cannot be handled and are
	// thrown away.
	Logger *slog.Logger

	// MaxMindDBReader is the maxmind database reader used to reolve IP
	// addresses to their countries using a maxmind GeoIP database.
	MaxMindDBReader *maxminddb.Reader
	// contains filtered or unexported fields
}

Notebrew represents a notebrew instance.

func New

func New() *Notebrew

New returns a new instance of Notebrew. Each field within it still needs to be manually configured.

func (*Notebrew) AccountDisabled

func (nbrew *Notebrew) AccountDisabled(w http.ResponseWriter, r *http.Request, disableReason string)

AccountDisabled indicates that a user's account is disabled.

func (*Notebrew) AddSecurityHeaders

func (nbrew *Notebrew) AddSecurityHeaders(w http.ResponseWriter)

func (*Notebrew) BadRequest

func (nbrew *Notebrew) BadRequest(w http.ResponseWriter, r *http.Request, serverErr error)

BadRequest indicates that something was wrong with the request data.

func (*Notebrew) Close

func (nbrew *Notebrew) Close() error

Close shuts down the notebrew instance as well as any background jobs it may have spawned.

func (*Notebrew) ContentBaseURL

func (nbrew *Notebrew) ContentBaseURL(sitePrefix string) string

ContentBaseURL returns the content site's base URL (starting with http:// or https://) for a given site prefix.

func (*Notebrew) ExecuteTemplate

func (nbrew *Notebrew) ExecuteTemplate(w http.ResponseWriter, r *http.Request, tmpl *template.Template, data any)

ExecuteTemplate renders a given template with the given data into the ResponseWriter, but it first buffers the HTML output so that it can detect if any template errors occurred, and if so return 500 Internal Server Error instead. Additionally, it does on-the-fly gzipping of the HTML response as well as calculating the ETag so that the HTML may be cached by the client.

func (*Notebrew) GetFlashSession

func (nbrew *Notebrew) GetFlashSession(w http.ResponseWriter, r *http.Request, valuePtr any) (ok bool, err error)

GetFlashSession retrieves a value from the user's flash session, unmarshals it into the valuePtr and then deletes the session. It returns a boolean result indicating if a flash session was retrieved.

func (*Notebrew) GetLogger

func (nbrew *Notebrew) GetLogger(ctx context.Context) *slog.Logger

GetLogger is a syntactic sugar operation for getting a request-specific logger from the context, or else it returns the default logger.

func (*Notebrew) GetReferer

func (nbrew *Notebrew) GetReferer(r *http.Request) string

GetReferer is like (*http.Request).Referer() except it returns an empty string if the referer is the same as the current page's URL so that the user doesn't keep pressing back to the same page.

func (*Notebrew) InternalServerError

func (nbrew *Notebrew) InternalServerError(w http.ResponseWriter, r *http.Request, serverErr error)

InternalServerError is a catch-all handler for catching server errors and displaying it to the user.

This includes the error message as well as the stack trace and notebrew version, in hopes that a user will be able to give developers the detailed error and trace in order to diagnose the problem faster.

func (*Notebrew) MethodNotAllowed

func (nbrew *Notebrew) MethodNotAllowed(w http.ResponseWriter, r *http.Request)

MethodNotAllowed indicates that the request method is not allowed.

func (*Notebrew) NotAuthenticated

func (nbrew *Notebrew) NotAuthenticated(w http.ResponseWriter, r *http.Request)

NotAuthenticated indicates that the user is not logged in.

func (*Notebrew) NotAuthorized

func (nbrew *Notebrew) NotAuthorized(w http.ResponseWriter, r *http.Request)

NotAuthorized indicates that the user is logged in, but is not authorized to view the current page or perform the current action.

func (*Notebrew) NotFound

func (nbrew *Notebrew) NotFound(w http.ResponseWriter, r *http.Request)

NotFound indicates that a URL does not exist.

func (*Notebrew) RedirectToHTTPS

func (nbrew *Notebrew) RedirectToHTTPS(w http.ResponseWriter, r *http.Request)

func (*Notebrew) RegenerateSite

func (nbrew *Notebrew) RegenerateSite(ctx context.Context, sitePrefix string) (RegenerationStats, error)

func (*Notebrew) ServeHTTP

func (nbrew *Notebrew) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Notebrew) SetFlashSession

func (nbrew *Notebrew) SetFlashSession(w http.ResponseWriter, r *http.Request, value any) error

SetFlashSession writes a value into the user's flash session.

func (*Notebrew) StorageLimitExceeded

func (nbrew *Notebrew) StorageLimitExceeded(w http.ResponseWriter, r *http.Request)

StorageLimitExceeded indicates that the user exceeded their storage limit.

func (*Notebrew) UnsupportedContentType

func (nbrew *Notebrew) UnsupportedContentType(w http.ResponseWriter, r *http.Request)

UnsupportedContentType indicates that the request did not send a supported Content-Type.

type ObjectStorage

type ObjectStorage interface {
	// Gets an object from a bucket.
	Get(ctx context.Context, key string) (io.ReadCloser, error)

	// Puts an object into a bucket. If key already exists, it should be
	// replaced.
	Put(ctx context.Context, key string, reader io.Reader) error

	// Deletes an object from a bucket. It returns no error if the object does
	// not exist.
	Delete(ctx context.Context, key string) error

	// Copies an object identified by srcKey into destKey. srcKey should exist.
	// If destKey already exists, it should be replaced.
	Copy(ctx context.Context, srcKey, destKey string) error
}

ObjectStorage represents an object storage provider.

type Page

type Page struct {
	// Parent URL of the page.
	Parent string

	// Name of the page.
	Name string

	// Title of the page.
	Title string
}

Page contains metadata about a page.

type PageData

type PageData struct {
	// Site information.
	Site Site

	// Parent URL of the page.
	Parent string

	// Name of the page.
	Name string

	// Title of the page.
	Title string

	// ChildPages of the page.
	ChildPages []Page

	// ContentMap of markdown file names to their file contents. Convert it to
	// HTML using the markdownToHTML template function.
	ContentMap map[string]string

	// Images belonging to the page.
	Images []Image

	// ModificationTime of the page.
	ModificationTime time.Time

	// CreationTime of the page.
	CreationTime time.Time
}

PageData is the data used to render a page.

type Pagination

type Pagination struct {
	// First page.
	First int

	// Previous page.
	Previous int

	// Current page.
	Current int

	// Next page.
	Next int

	// Last page.
	Last int

	// Numbers is the list of page numbers to display.
	Numbers []int
}

Pagination represents the pagination information for a post list.

func NewPagination

func NewPagination(currentPage, lastPage, visiblePages int) Pagination

NewPagination constructs a new pagination for a given range of pages.

type Post

type Post struct {
	// Category of the post.
	Category string

	// Name of the post.
	Name string

	// Title of the post.
	Title string

	// Preview of the post.
	Preview string

	// Whether the post has more text after the preview.
	HasMore bool

	// Content of the post.
	Content string

	// Images belonging to the post.
	Images []Image

	// CreationTime of the post.
	CreationTime time.Time

	// ModificationTime of the post.
	ModificationTime time.Time
	// contains filtered or unexported fields
}

Post contains metadata about a post.

type PostData

type PostData struct {
	// Site information.
	Site Site

	// Category of the post.
	Category string

	// Name of the post.
	Name string

	// Title of the post.
	Title string

	// Content of the post.
	Content string

	// Images belonging to the post that are not already explicitly included in
	// the post content.
	Images []Image

	// CreationTime of the post.
	CreationTime time.Time

	// ModificationTime of the post.
	ModificationTime time.Time
}

PostData is the data used to render a post.

type PostListData

type PostListData struct {
	// Site information.
	Site Site

	// Category of the post list.
	Category string

	// Pagination information for the post list.
	Pagination Pagination

	// The lists of posts for the current page of the post list.
	Posts []Post
}

PostListData is the data used to render a post list page.

type RegenerationStats

type RegenerationStats struct {
	Count         int64         `json:"count"`
	TimeTaken     string        `json:"timeTaken"`
	TemplateError TemplateError `json:"templateError"`
}

type ReplicatedFS

type ReplicatedFS struct {
	// Leader is the primary FS that we read from and write to.
	Leader FS

	// Followers are the secondary FSes that writes are replicated to.
	Followers []FS

	// If SynchronousReplication is true, writes will be synchronously
	// replicated to the followers.
	SynchronousReplication bool

	// (Required) Logger is used for reporting errors that cannot be handled
	// and are thrown away.
	Logger *slog.Logger
	// contains filtered or unexported fields
}

SFTPFS implements a writeable filesystem on a leader FS and zero or more follower FSes. All reads come from the leader FS, while all writes will be sent to the leader FS as well as the follower FSes (synchronously or asynchronously depending on the SynchronousReplication field).

func NewReplicatedFS

func NewReplicatedFS(config ReplicatedFSConfig) (*ReplicatedFS, error)

NewReplicatedFS constructs a new ReplicatedFS.

func (*ReplicatedFS) As

func (fsys *ReplicatedFS) As(target any) bool

As calls the `As(any) bool` method on the leader FS if it exists.

func (*ReplicatedFS) Close

func (fsys *ReplicatedFS) Close() error

Close initiates the shutdown of any background jobs currently synchronizing to the follower FSes and returns when all background jobs have completed.

func (*ReplicatedFS) Copy

func (fsys *ReplicatedFS) Copy(srcName, destName string) error

Copy implements the Copy FS operation for ReplicatedFS.

func (*ReplicatedFS) Mkdir

func (fsys *ReplicatedFS) Mkdir(name string, perm fs.FileMode) error

Mkdir implements the Mkdir FS operation for ReplicatedFS.

func (*ReplicatedFS) MkdirAll

func (fsys *ReplicatedFS) MkdirAll(name string, perm fs.FileMode) error

MkdirAll implements the MkdirAll FS operation for ReplicatedFS.

func (*ReplicatedFS) Open

func (fsys *ReplicatedFS) Open(name string) (fs.File, error)

Open implements the Open FS operation for SFTPFS.

func (*ReplicatedFS) OpenWriter

func (fsys *ReplicatedFS) OpenWriter(name string, perm fs.FileMode) (io.WriteCloser, error)

OpenWriter implements the OpenWriter FS operation for ReplicatedFS.

func (*ReplicatedFS) ReadDir

func (fsys *ReplicatedFS) ReadDir(name string) ([]fs.DirEntry, error)

ReadDir implements the ReadDir FS operation for ReplicatedFS.

func (*ReplicatedFS) Remove

func (fsys *ReplicatedFS) Remove(name string) error

Remove implements the Remove FS operation for ReplicatedFS.

func (*ReplicatedFS) RemoveAll

func (fsys *ReplicatedFS) RemoveAll(name string) error

RemoveAll implements the RemoveAll FS operation for ReplicatedFS.

func (*ReplicatedFS) Rename

func (fsys *ReplicatedFS) Rename(oldName, newName string) error

Rename implements the Rename FS operation for ReplicatedFS.

func (*ReplicatedFS) Stat

func (fsys *ReplicatedFS) Stat(name string) (fs.FileInfo, error)

Stat implements the fs.StatFS interface.

func (*ReplicatedFS) WithContext

func (fsys *ReplicatedFS) WithContext(ctx context.Context) FS

WithContext returns a new FS with the given context.

func (*ReplicatedFS) WithValues

func (fsys *ReplicatedFS) WithValues(values map[string]any) FS

WithValues returns a new ReplicatedFS where the leader FS and each follower FS has `WithValues(any) FS` method called on it if it exists.

type ReplicatedFSConfig

type ReplicatedFSConfig struct {
	// (Required) Leader is the primary FS that we read from and write to.
	Leader FS

	// Followers are the secondary FSes that writes are replicated to.
	Followers []FS

	// If SynchronousReplication is true, writes will be synchronously
	// replicated to the followers.
	SynchronousReplication bool

	// (Required) Logger is used for reporting errors that cannot be handled
	// and are thrown away.
	Logger *slog.Logger
}

ReplicatedFSConfig holds the parameters needed to construct a ReplicatedFS.

type ReplicatedFileWriter

type ReplicatedFileWriter struct {
	// contains filtered or unexported fields
}

ReplicatedFileWriter represents a writable file on a ReplicatedFS.

func (*ReplicatedFileWriter) Close

func (file *ReplicatedFileWriter) Close() error

Close saves the contents of the ReplicatedFileWriter to the leader FS and follower FSes and closes the ReplicatedFileWriter.

func (*ReplicatedFileWriter) Write

func (file *ReplicatedFileWriter) Write(p []byte) (n int, err error)

Write writes len(b) bytes from b to the ReplicatedFileWriter. It returns the number of bytes written and an error, if any. Write returns a non-nil error when n != len(b).

type S3ObjectStorage

type S3ObjectStorage struct {
	// S3 SDK client.
	Client *s3.Client

	// S3 Bucket to put objects in.
	Bucket string

	// File extension to Content-Type map.
	ContentTypeMap map[string]string

	// Logger is used for reporting errors that cannot be handled and are
	// thrown away.
	Logger *slog.Logger
}

S3ObjectStorage implements ObjectStorage via an S3-compatible provider.

func NewS3Storage

func NewS3Storage(ctx context.Context, config S3StorageConfig) (*S3ObjectStorage, error)

NewS3Storage constructs a new S3ObjectStorage.

func (*S3ObjectStorage) Copy

func (storage *S3ObjectStorage) Copy(ctx context.Context, srcKey, destKey string) error

Copy implements the Copy ObjectStorage operation for S3ObjectStorage.

func (*S3ObjectStorage) Delete

func (storage *S3ObjectStorage) Delete(ctx context.Context, key string) error

Delete implements the Delete ObjectStorage operation for S3ObjectStorage.

func (*S3ObjectStorage) Get

func (storage *S3ObjectStorage) Get(ctx context.Context, key string) (io.ReadCloser, error)

Get implements the Get ObjectStorage operation for S3ObjectStorage.

func (*S3ObjectStorage) Put

func (storage *S3ObjectStorage) Put(ctx context.Context, key string, reader io.Reader) error

Put implements the Put ObjectStorage operation for S3ObjectStorage.

type S3StorageConfig

type S3StorageConfig struct {
	// (Required) S3 endpoint.
	Endpoint string

	// (Required) S3 region.
	Region string

	// (Required) S3 bucket.
	Bucket string

	// (Required) S3 access key ID.
	AccessKeyID string

	// (Required) S3 secret access key.
	SecretAccessKey string

	// File extension to Content-Type map.
	ContentTypeMap map[string]string

	// (Required) Logger is used for reporting errors that cannot be handled
	// and are thrown away.
	Logger *slog.Logger
}

S3StorageConfig holds the parameters needed to construct an S3ObjectStorage.

type SFTPClient

type SFTPClient struct {
	// contains filtered or unexported fields
}

SFTPClient is a wrapper around an *sftp.Client which records the state of whether the *sftp.Client is disconnected.

func (*SFTPClient) Get

func (client *SFTPClient) Get(newSSHClient func() (*ssh.Client, error)) (*sftp.Client, error)

Get returns the underlying *sftp.Client if it is connected, or else it instantiates a new one using the provided ssh.Client constructor function.

type SFTPFS

type SFTPFS struct {
	// ssh.Client constructor function.
	NewSSHClient func() (*ssh.Client, error)

	// Root directory on the SFTP server.
	RootDir string

	// Temp directory on the SFTP server.
	TempDir string

	// Logger is used for reporting errors that cannot be handled and are
	// thrown away.
	Logger *slog.Logger

	// Clients is the list of available sftp Clients we can use.
	Clients []*SFTPClient
	// contains filtered or unexported fields
}

SFTPFS implements a writeable filesystem using an SFTP server. It maintains one or more persistent connections to an SFTP server.

func NewSFTPFS

func NewSFTPFS(config SFTPFSConfig) (*SFTPFS, error)

NewSFTPFS constructs a new SFTPFS.

func (*SFTPFS) As

func (fsys *SFTPFS) As(target any) bool

As writes the current SFTPFS into the target if it is a valid SFTPFS pointer.

func (*SFTPFS) Close

func (fsys *SFTPFS) Close() error

Close closes all SFTP connections in the SFTPFS.

func (*SFTPFS) Copy

func (fsys *SFTPFS) Copy(srcName, destName string) error

Copy implements the Copy FS operation for SFTPFS.

func (*SFTPFS) Mkdir

func (fsys *SFTPFS) Mkdir(name string, _ fs.FileMode) error

Mkdir implements the Mkdir FS operation for SFTPFS.

func (*SFTPFS) MkdirAll

func (fsys *SFTPFS) MkdirAll(name string, _ fs.FileMode) error

MkdirAll implements the MkdirAll FS operation for SFTPFS.

func (*SFTPFS) Open

func (fsys *SFTPFS) Open(name string) (fs.File, error)

Open implements the Open FS operation for SFTPFS.

func (*SFTPFS) OpenWriter

func (fsys *SFTPFS) OpenWriter(name string, perm fs.FileMode) (io.WriteCloser, error)

OpenWriter implements the OpenWriter FS operation for DatabaseFS.

func (*SFTPFS) ReadDir

func (fsys *SFTPFS) ReadDir(name string) ([]fs.DirEntry, error)

ReadDir implements the ReadDir FS operation for SFTPFS.

func (*SFTPFS) Remove

func (fsys *SFTPFS) Remove(name string) error

Remove implements the Remove FS operation for SFTPFS.

func (*SFTPFS) RemoveAll

func (fsys *SFTPFS) RemoveAll(name string) error

RemoveAll implements the RemoveAll FS operation for STFPFS.

func (*SFTPFS) Rename

func (fsys *SFTPFS) Rename(oldName, newName string) error

Rename implements the Rename FS operation for SFTPFS.

func (*SFTPFS) Stat

func (fsys *SFTPFS) Stat(name string) (fs.FileInfo, error)

Stat implements the fs.StatFS interface.

func (*SFTPFS) WithContext

func (fsys *SFTPFS) WithContext(ctx context.Context) FS

WithContext returns a new FS with the given context.

type SFTPFSConfig

type SFTPFSConfig struct {
	// (Required) ssh.Client constructor function.
	NewSSHClient func() (*ssh.Client, error)

	// (Required) Root directory on the SFTP server.
	RootDir string

	// Temp directory on the SFTP server.
	TempDir string

	// Max open connections to the SFTP server.
	MaxOpenConns int

	// (Required) Logger is used for reporting errors that cannot be handled
	// and are thrown away.
	Logger *slog.Logger
}

SFTPFSConfig holds the parameters needed to construct a SFTPFS.

type SFTPFileWriter

type SFTPFileWriter struct {
	// contains filtered or unexported fields
}

SFTPFileWriter represents a writable file on an SFTPFS.

func (*SFTPFileWriter) Close

func (file *SFTPFileWriter) Close() error

Close saves the contents of the SFTPFileWriter to a file on the SFTP server and closes the SFTPFileWriter.

func (*SFTPFileWriter) ReadFrom

func (file *SFTPFileWriter) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom implements io.ReaderFrom.

func (*SFTPFileWriter) Write

func (file *SFTPFileWriter) Write(p []byte) (n int, err error)

Write writes len(b) bytes from b to the SFTPFileWriter. It returns the number of bytes written and an error, if any. Write returns a non-nil error when n != len(b).

type Site

type Site struct {
	// Language code of the site.
	LanguageCode string

	// Title of the site.
	Title string

	// Tagline of the site.
	Tagline string

	// Favicon of the site.
	Favicon template.URL

	// The site's timezone offset in seconds.
	TimezoneOffsetSeconds int

	// Description of the site.
	Description string

	// NavigationLinks of the site.
	NavigationLinks []NavigationLink
}

Site holds the global properties of a site.

type SiteConfig

type SiteConfig struct {
	LanguageCode    string           `json:"languageCode"`
	Title           string           `json:"title"`
	Tagline         string           `json:"tagline"`
	Emoji           string           `json:"emoji"`
	Favicon         string           `json:"favicon"`
	CodeStyle       string           `json:"codeStyle"`
	TimezoneOffset  string           `json:"timezoneOffset"`
	Description     string           `json:"description"`
	NavigationLinks []NavigationLink `json:"navigationLinks"`
}

type SiteGenerator

type SiteGenerator struct {
	// Site information.
	Site Site
	// contains filtered or unexported fields
}

SiteGenerator is used to generate pages and posts for a particular site.

func NewSiteGenerator

func NewSiteGenerator(ctx context.Context, siteGenConfig SiteGeneratorConfig) (*SiteGenerator, error)

NewSiteGenerator constructs a new SiteGenerator.

func (*SiteGenerator) GeneratePage

func (siteGen *SiteGenerator) GeneratePage(ctx context.Context, filePath, text string, modTime, creationTime time.Time) error

GeneratePage generates a page. filePath is the file path to the source .html page in the pages/ directory, text is the source text of the page, modTime and creationTime are when the page was modified and created.

func (*SiteGenerator) GeneratePost

func (siteGen *SiteGenerator) GeneratePost(ctx context.Context, filePath, text string, modTime, creationTime time.Time, tmpl *template.Template) error

GeneratePost generates a post. filePath is the file path to the source post in the posts/ directory and text is the text of the source post.

func (*SiteGenerator) GeneratePostList

func (siteGen *SiteGenerator) GeneratePostList(ctx context.Context, category string, tmpl *template.Template) (int64, error)

GeneratePostList generates the post list for a category, which contain one or more post list pages.

func (*SiteGenerator) GeneratePostListPage

func (siteGen *SiteGenerator) GeneratePostListPage(ctx context.Context, category string, tmpl *template.Template, lastPage, currentPage int, posts []Post) error

GeneratePostListPage generates a singular page of the post list for a category.

func (*SiteGenerator) GeneratePosts

func (siteGen *SiteGenerator) GeneratePosts(ctx context.Context, category string, tmpl *template.Template) (int64, error)

GeneratePosts generates all posts for a given category.

func (*SiteGenerator) ParseTemplate

func (siteGen *SiteGenerator) ParseTemplate(ctx context.Context, name, text string) (*template.Template, error)

ParseTemplate parses text as a template body for name. Template references starting with "/themes/" and ending in ".html" will be looked for in the site's themes folder.

func (*SiteGenerator) PostListTemplate

func (siteGen *SiteGenerator) PostListTemplate(ctx context.Context, category string) (*template.Template, error)

PostListTemplate returns the post list template for a category.

func (*SiteGenerator) PostTemplate

func (siteGen *SiteGenerator) PostTemplate(ctx context.Context, category string) (*template.Template, error)

PostTemplate returns the post template for a category.

type SiteGeneratorConfig

type SiteGeneratorConfig struct {
	// FS is the filesystem that holds the site content.
	FS FS

	// ContentDomain is the domain that the site's generated content is served
	// on.
	ContentDomain string

	// ContentDomainHTTPS indicates whether the content domain is served over
	// HTTPS.
	ContentDomainHTTPS bool

	// CDNDomain is the domain of the CDN that is used for hosting images.
	CDNDomain string

	// SitePrefix is the site prefix of the site.
	SitePrefix string
}

SiteGeneratorConfig holds the parameters needed to construct a new SiteGenerator.

type TemplateError

type TemplateError struct {
	// Name of the template that caused the error.
	Name string `json:"name"`

	// Line number in the template that caused the error.
	Line int `json:"line"`

	// ErrorMessage of the error.
	ErrorMessage string `json:"errorMessage"`
}

TemplateError is used to represent an error when parsing or executing a template.

func (TemplateError) Error

func (templateErr TemplateError) Error() string

Error implements the error interface.

type User

type User struct {
	// UserID uniquely identifies a user. It cannot be changed.
	UserID ID

	// Username uniquely identifies a user. It can be changed.
	Username string

	// Email uniquely identifies a user. It can be changed.
	Email string

	// TimezoneOffsetSeconds represents a user's preferred timezone offset in
	// seconds.
	TimezoneOffsetSeconds int

	// Is not empty, DisableReason is the reason why the user's account is
	// marked as disabled.
	DisableReason string

	// SiteLimit is the limit on the number of sites the user can create.
	SiteLimit int64

	// StorageLimit is the limit on the amount of storage the user can use.
	StorageLimit int64

	// UserFlags are various properties on a user that may be enabled or
	// disabled e.g. UploadImages.
	UserFlags map[string]bool
}

User represents a user in the users table.

Directories

Path Synopsis
internal
highlighting
https://github.com/yuin/goldmark-highlighting/tree/v2
https://github.com/yuin/goldmark-highlighting/tree/v2

Jump to

Keyboard shortcuts

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