Help us shape the future of Go! Take the 2026 Go Developer Survey

patchbin

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 51 Imported by: 0

README

patchbin

A pastebin for patches, supercharged for git collaboration.

Contributions are designed to be anonymous: the quality of your work is what matters. No signup required, just connect with an SSH key.

The target project doesn't need to run patchbin for someone to submit a patch request against it. It works like a pull request, except both sides collaborate by sending rounds of patchsets: a contributor sends patches, a reviewer replies with their own patches on top, back and forth, as commits rather than comments. The result is a collaborative workspace built entirely out of patches. Reviewing means pulling the code down, not clicking through a diff viewer. Issues work the same way: an issue is just a patch request without any code attached yet, and anyone can follow up with a real patch request on top of it.

There's no accept or reject step. A patch request is simply active or inactive: active ones go inactive after 30 days without activity. When a reviewer is happy with the code, they pull it, merge it, and push upstream themselves; there's nothing to manage here beyond that.

quickstart

Submit a patch request (starts as a draft, visible only to you):

git format-patch main --stdout | ssh {url} pr create {repo}

Open it so others can see it (also enables RSS notifications):

ssh {url} pr open {prID}

Checkout the latest patchset from a patch request:

ssh {url} print pr-{prID} | git am -3

Add a follow-up patchset (e.g. after addressing review comments):

git format-patch main --stdout | ssh {url} pr add {prID}

Help guide:

ssh {url} help

commands

pr - manage patch requests
  • pr create {repo} - submit a new PR from stdin (starts as draft)
    git format-patch main --stdout | ssh {url} pr create {repo}
    
  • pr add {prID} - add a new patchset to an existing PR from stdin
    git format-patch main --stdout | ssh {url} pr add {prID}
    
  • pr open {prID} [--comment] - transition draft open, enables RSS notifications
    ssh {url} pr open {prID}
    
  • pr draft {prID} [--comment] - transition open draft, disables RSS notifications
    ssh {url} pr draft {prID}
    
  • pr edit {prID} {title} - rename a PR
    ssh {url} pr edit {prID} "new title"
    
  • pr summary {prID} - show metadata, patchsets, and patches for a PR
    ssh {url} pr summary {prID}
    
  • pr ls [repo] [--draft|--open|--active|--inactive|--mine] - list PRs
    ssh {url} pr ls {repo} --open
    
issue - text-only patch requests
  • issue create {repo} [--title] - submit a new issue from stdin (starts as open)
    echo "steps to reproduce..." | ssh {url} issue create {repo} --title "bug: crash on startup"
    
ps - manage patchsets
  • ps rm {patchsetID} - remove a patchset and its patches (creator only)
    ssh {url} ps rm ps-{patchsetID}
    
print - print patches for checkout
  • print pr-{prID} - print the latest patchset for a PR
    ssh {url} print pr-{prID} | git am -3
    
  • print ps-{patchsetID} - print a specific patchset
    ssh {url} print ps-{patchsetID} | git am -3
    
logs - event history
  • logs [--pr ID] [--pubkey] - list event logs, optionally filtered to a PR or your own activity
    ssh {url} logs --pr {prID}
    

self-hosting

patchbin needs a patchbin.toml config file and a data directory (for the sqlite db and SSH host keys).

Copy or create a patchbin.toml file inside a ./data directory:

mkdir -p data
cp patchbin.toml ./data/patchbin.toml
vim ./data/patchbin.toml
docker-compose

The included docker-compose.yml pulls the published image and mounts a local data directory:

services:
  patchbin:
    image: ghcr.io/picosh/pico/patchbin:latest
    restart: always
    volumes:
      - ./data/patchbin/data:/app/data

Place patchbin.toml inside ./data/patchbin/data, then run:

docker compose up -d
docker image

Run the image directly, mounting your data directory to /app/data:

docker run -d -v ./data:/app/data ghcr.io/picosh/pico/patchbin:latest

patchbin.toml must live inside the mounted ./data directory, since that's the default config path the binary looks for.

from go source

Clone the repo, then build and run the binary:

make build
./build/patchbin --config ./data/patchbin.toml

Or without the Makefile:

go build -o ./build/patchbin ./cmd/patchbin
./build/patchbin --config ./data/patchbin.toml
done

Access the SSH app:

ssh -p 2222 localhost help

Access the web app:

curl localhost:3000

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	COST_MAX                           = 65536
	RANGE_DIFF_CREATION_FACTOR_DEFAULT = 60
)
View Source
var ErrNotPrOwner = fmt.Errorf("only the PR creator can perform this action")
View Source
var ErrPatchExists = errors.New("patch already exists for patch request")
View Source
var GITPR_VERSION = "2026.02.25"

Functions

func AugmentCoverLetterPatch

func AugmentCoverLetterPatch(rawText string, discussion string, cfgURL string, prID int64) string

AugmentCoverLetterPatch appends References trailer and discussion to an existing cover letter patch. Preserves original content.

func BuildDiscussion

func BuildDiscussion(events []*EventLog, users map[int64]*User) string

BuildDiscussion formats event logs into a plain-text discussion thread. Uses SSH pubkey fingerprints for user identity. Interleaves "Submitted revision ps-X" lines for patchset events.

func GenerateCoverLetterPatch

func GenerateCoverLetterPatch(pr *PatchRequest, discussion string, cfgURL string) string

GenerateCoverLetterPatch creates a cover letter patch in mbox format. Empty tree, PR title as subject, References trailer + discussion in body.

func GenerateMboxWithCoverLetter

func GenerateMboxWithCoverLetter(pr *PatchRequest, patches []*Patch,
	events []*EventLog, users map[int64]*User, cfgURL string,
) string

GenerateMboxWithCoverLetter returns the full mbox: cover letter + patches. If the first patch is already a cover letter, augments it with References + discussion. If not, generates a new cover letter from the PR name.

func GetAuthorizedKeys

func GetAuthorizedKeys(pubkeys []string) ([]ssh.PublicKey, error)

func GitPatchRequestMiddleware

func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) pssh.SSHServerMiddleware

func GitSshServer

func GitSshServer(ctx context.Context, cfg *GitCfg) *pssh.SSHServer

func GitWebServer

func GitWebServer(cfg *GitCfg) http.Handler

func HasCoverLetter

func HasCoverLetter(patches []*Patch) bool

HasCoverLetter checks if the first patch is a cover letter (no diff).

func LoadConfigFile

func LoadConfigFile(fpath string, logger *slog.Logger)

func NewCli

func NewCli(sesh *pssh.SSHServerConnSession, be *Backend, pr GitPatchRequest) *cli.App

func NewTabWriter

func NewTabWriter(out io.Writer) *tabwriter.Writer

func ParsePatch

func ParsePatch(patchRaw string) ([]*gitdiff.File, string, error)

func RangeDiffToStr

func RangeDiffToStr(diffs []*RangeDiffOutput) string

RangeDiffToStr returns a simple string representation of the range diff.

func SqliteOpen

func SqliteOpen(dsn string, logger *slog.Logger) (*sqlx.DB, error)

Open opens a database connection.

func SupportsSemanticDiff

func SupportsSemanticDiff(fileName string) bool

SupportsSemanticDiff reports whether a file name's extension has a registered language spec, i.e. whether AnalyzeSemanticChanges can produce anything better than an empty result for it.

Types

type Acl

type Acl struct {
	ID         int64          `db:"id"`
	Pubkey     sql.NullString `db:"pubkey"`
	IpAddress  sql.NullString `db:"ip_address"`
	Permission string         `db:"permission"`
	CreatedAt  time.Time      `db:"created_at"`
}

Acl is a db model for access control.

type AllPatchData

type AllPatchData struct {
	Patches   []PatchSummary
	Patchsets []*PatchsetData
}

type Backend

type Backend struct {
	Logger  *slog.Logger
	DB      *sqlx.DB
	Cfg     *GitCfg
	Limiter *RateLimiter
}

func (*Backend) ComputeUserName

func (be *Backend) ComputeUserName(pubkey string) string

ComputeUserName derives a username from an SSH public key. Uses the first 8 characters of the SHA256 hash of the key.

func (*Backend) IsAdmin

func (be *Backend) IsAdmin(pk ssh.PublicKey) bool

func (*Backend) KeyForFingerprint

func (be *Backend) KeyForFingerprint(pk ssh.PublicKey) string

func (*Backend) KeyForKeyText

func (be *Backend) KeyForKeyText(pk ssh.PublicKey) string

func (*Backend) KeysEqual

func (be *Backend) KeysEqual(pka, pkb string) bool

func (*Backend) Pubkey

func (be *Backend) Pubkey(pk ssh.PublicKey) string

Pubkey returns the standardized public key string for SSH.

func (*Backend) PubkeyToPublicKey

func (be *Backend) PubkeyToPublicKey(pubkey string) (ssh.PublicKey, error)

func (*Backend) PublicKeysEqual

func (be *Backend) PublicKeysEqual(a, b ssh.PublicKey) bool

type BasicData

type BasicData struct {
	MetaData
}

type EventData

type EventData struct {
	Name    string `json:"name,omitempty"`
	Status  Status `json:"status,omitempty"`
	Comment string `json:"comment,omitempty"`
}

func (*EventData) Scan

func (e *EventData) Scan(value any) error

func (EventData) String

func (e EventData) String() string

func (EventData) Value

func (e EventData) Value() (driver.Value, error)

type EventLog

type EventLog struct {
	ID             int64         `db:"id"`
	UserID         int64         `db:"user_id"`
	PatchRequestID sql.NullInt64 `db:"patch_request_id"`
	PatchsetID     sql.NullInt64 `db:"patchset_id"`
	Event          string        `db:"event"`
	CreatedAt      time.Time     `db:"created_at"`
	Data           EventData     `db:"data"`
}

EventLog is a event log for RSS or other notification systems.

type EventLogData

type EventLogData struct {
	*EventLog
	UserData
	*Patchset
	FormattedPatchsetID string
	Date                string
	RangeDiff           []*RangeDiffOutput
}

type GitCfg

type GitCfg struct {
	DataDir           string          `koanf:"data_dir"`
	Url               string          `koanf:"url"`
	Host              string          `koanf:"host"`
	SshPort           string          `koanf:"ssh_port"`
	WebPort           string          `koanf:"web_port"`
	PromPort          string          `koanf:"prom_port"`
	AdminsStr         []string        `koanf:"admins"`
	Admins            []ssh.PublicKey `koanf:"admins_pk"`
	Theme             string          `koanf:"theme"`
	TimeFormat        string          `koanf:"time_format"`
	Desc              string          `koanf:"desc"`
	RateLimitCount    int             `koanf:"rate_limit_count"`
	RateLimitInterval string          `koanf:"rate_limit_interval"`
	MaxStdinBytes     int64           `koanf:"max_stdin_bytes"`
	Logger            *slog.Logger
}

func NewGitCfg

func NewGitCfg(logger *slog.Logger) *GitCfg

type GitPatchRequest

type GitPatchRequest interface {
	GetUsers() ([]*User, error)
	GetUserByID(userID int64) (*User, error)
	GetUserByPubkey(pubkey string) (*User, error)
	UpsertUserByPubkey(pubkey string) (*User, error)
	IsBanned(pubkey, ipAddress string) error
	SubmitPatchRequest(userID int64, userPubkey string, repoName string, patchset io.Reader) (*PatchRequest, error)
	SubmitPatchset(prID, userID int64, op PatchsetOp, patchset io.Reader) ([]*Patch, error)
	GetPatchRequestByID(prID int64) (*PatchRequest, error)
	GetPatchRequests() ([]*PatchRequest, error)
	GetPatchRequestsByRepoName(repoName string) ([]*PatchRequest, error)
	GetPatchRequestsByPubkey(pubkey string) ([]*PatchRequest, error)
	GetPatchsetsByPrID(prID int64) ([]*Patchset, error)
	GetPatchsetByID(patchsetID int64) (*Patchset, error)
	GetLatestPatchsetByPrID(prID int64) (*Patchset, error)
	GetPatchesByPatchsetID(patchsetID int64) ([]*Patch, error)
	UpdatePatchRequestStatus(prID int64, userPubkey string, status Status, comment string) error
	UpdatePatchRequestName(prID int64, userPubkey string, name string) error
	DeletePatchsetByID(userID, prID int64, patchsetID int64) error
	SubmitIssue(userID int64, userPubkey string, repoName, title, body string) (*PatchRequest, error)
	CreateEventLog(tx *sqlx.Tx, eventLog EventLog) error
	GetEventLogs() ([]*EventLog, error)
	GetEventLogsByPrID(prID int64) ([]*EventLog, error)
	GetEventLogsByUserID(userID int64) ([]*EventLog, error)
	DiffPatchsets(aset *Patchset, bset *Patchset) ([]*RangeDiffOutput, error)
}

type MetaData

type MetaData struct {
	URL  string
	Desc template.HTML
	Tab  TabStatus
}

type Patch

type Patch struct {
	ID            int64          `db:"id"`
	UserID        int64          `db:"user_id"`
	PatchsetID    int64          `db:"patchset_id"`
	AuthorName    string         `db:"author_name"`
	AuthorEmail   string         `db:"author_email"`
	AuthorDate    time.Time      `db:"author_date"`
	Title         string         `db:"title"`
	Body          string         `db:"body"`
	BodyAppendix  string         `db:"body_appendix"`
	CommitSha     string         `db:"commit_sha"`
	ContentSha    string         `db:"content_sha"`
	BaseCommitSha sql.NullString `db:"base_commit_sha"`
	RawText       string         `db:"raw_text"`
	CreatedAt     time.Time      `db:"created_at"`
	Files         []*gitdiff.File
}

Patch is a database model for a single entry in a patchset. This usually corresponds to a git commit.

func ParsePatchset

func ParsePatchset(patchset io.Reader) ([]*Patch, error)

func (*Patch) CalcDiff

func (p *Patch) CalcDiff() string

type PatchData

type PatchData struct {
	*Patch
	PatchFiles          []*PatchFile
	PatchHeader         *gitdiff.PatchHeader
	Url                 template.URL
	FormattedAuthorDate string
	SemanticSummary     SemanticSummary
}

type PatchFile

type PatchFile struct {
	*gitdiff.File
	DisplayName     string
	FileAnchor      string
	Adds            int64
	Dels            int64
	Hunks           []PatchHunk
	SemanticChanges []SemanticChange
}

type PatchHunk

type PatchHunk struct {
	Anchor   string
	DiffText template.HTML
}

type PatchRequest

type PatchRequest struct {
	ID           int64     `db:"id"`
	UserID       int64     `db:"user_id"`
	RepoName     string    `db:"repo_name"` // Plain string namespace
	Name         string    `db:"name"`
	Text         string    `db:"text"`
	Status       Status    `db:"status"`
	CreatedAt    time.Time `db:"created_at"`
	UpdatedAt    time.Time `db:"updated_at"`
	LastActivity time.Time `db:"last_activity"`
}

PatchRequest is a database model for patches submitted to a Repo.

type PatchSummary

type PatchSummary struct {
	*Patch
	Url                 template.URL
	FormattedAuthorDate string
}

PatchSummary is a lightweight view of a patch used for the commit list, without the expensive diff parsing/rendering that a full PatchData needs.

type Patchset

type Patchset struct {
	ID             int64     `db:"id"`
	UserID         int64     `db:"user_id"`
	PatchRequestID int64     `db:"patch_request_id"`
	Review         bool      `db:"review"`
	CreatedAt      time.Time `db:"created_at"`
}

type PatchsetData

type PatchsetData struct {
	*Patchset
	UserData
	FormattedID string
	Date        string
	RangeDiff   []*RangeDiffOutput
}

type PatchsetOp

type PatchsetOp int
const (
	OpNormal PatchsetOp = iota
)

type PrCmd

type PrCmd struct {
	Backend *Backend
}

func (PrCmd) CreateEventLog

func (cmd PrCmd) CreateEventLog(tx *sqlx.Tx, eventLog EventLog) error

func (PrCmd) DeletePatchsetByID

func (cmd PrCmd) DeletePatchsetByID(userID int64, prID int64, patchsetID int64) error

func (PrCmd) DiffPatchsets

func (cmd PrCmd) DiffPatchsets(prev *Patchset, next *Patchset) ([]*RangeDiffOutput, error)

func (PrCmd) GetEventLogs

func (cmd PrCmd) GetEventLogs() ([]*EventLog, error)

func (PrCmd) GetEventLogsByPrID

func (cmd PrCmd) GetEventLogsByPrID(prID int64) ([]*EventLog, error)

func (PrCmd) GetEventLogsByUserID

func (cmd PrCmd) GetEventLogsByUserID(userID int64) ([]*EventLog, error)

func (PrCmd) GetLatestPatchsetByPrID

func (pr PrCmd) GetLatestPatchsetByPrID(prID int64) (*Patchset, error)

func (PrCmd) GetPatchRequestByID

func (cmd PrCmd) GetPatchRequestByID(prID int64) (*PatchRequest, error)

func (PrCmd) GetPatchRequests

func (cmd PrCmd) GetPatchRequests() ([]*PatchRequest, error)

func (PrCmd) GetPatchRequestsActive

func (cmd PrCmd) GetPatchRequestsActive() ([]*PatchRequest, error)

func (PrCmd) GetPatchRequestsByPubkey

func (cmd PrCmd) GetPatchRequestsByPubkey(pubkey string) ([]*PatchRequest, error)

func (PrCmd) GetPatchRequestsByRepoName

func (cmd PrCmd) GetPatchRequestsByRepoName(repoName string) ([]*PatchRequest, error)

func (PrCmd) GetPatchRequestsByStatus

func (cmd PrCmd) GetPatchRequestsByStatus(status Status) ([]*PatchRequest, error)

func (PrCmd) GetPatchRequestsInactive

func (cmd PrCmd) GetPatchRequestsInactive() ([]*PatchRequest, error)

func (PrCmd) GetPatchesByPatchsetID

func (pr PrCmd) GetPatchesByPatchsetID(patchsetID int64) ([]*Patch, error)

func (PrCmd) GetPatchsetByID

func (pr PrCmd) GetPatchsetByID(patchsetID int64) (*Patchset, error)

func (PrCmd) GetPatchsetsByPrID

func (pr PrCmd) GetPatchsetsByPrID(prID int64) ([]*Patchset, error)

func (PrCmd) GetUserByID

func (pr PrCmd) GetUserByID(id int64) (*User, error)

func (PrCmd) GetUserByPubkey

func (pr PrCmd) GetUserByPubkey(pubkey string) (*User, error)

func (PrCmd) GetUsers

func (pr PrCmd) GetUsers() ([]*User, error)

func (PrCmd) IsBanned

func (pr PrCmd) IsBanned(pubkey, ipAddress string) error

func (PrCmd) SubmitIssue

func (cmd PrCmd) SubmitIssue(userID int64, userPubkey string, repoName, title, body string) (*PatchRequest, error)

SubmitIssue creates a new patch request as an issue (text-only, no patches, starts open). The title is the issue subject, body is the full description.

func (PrCmd) SubmitPatchRequest

func (cmd PrCmd) SubmitPatchRequest(userID int64, userPubkey string, repoName string, patchset io.Reader) (*PatchRequest, error)

SubmitPatchRequest creates a new patch request with draft status.

func (PrCmd) SubmitPatchset

func (cmd PrCmd) SubmitPatchset(prID int64, userID int64, op PatchsetOp, patchset io.Reader) ([]*Patch, error)

func (PrCmd) UpdatePatchRequestName

func (cmd PrCmd) UpdatePatchRequestName(prID int64, userPubkey string, name string) error

UpdatePatchRequestName changes the PR title. Only the PR creator (by pubkey) can do this.

func (PrCmd) UpdatePatchRequestStatus

func (cmd PrCmd) UpdatePatchRequestStatus(prID int64, userPubkey string, status Status, comment string) error

UpdatePatchRequestStatus changes the PR status. Only the PR creator (by pubkey) can do this.

func (PrCmd) UpsertUserByPubkey

func (pr PrCmd) UpsertUserByPubkey(pubkey string) (*User, error)

type PrData

type PrData struct {
	UserData
	ID     int64
	Title  string
	Date   string
	Status Status
}

type PrDetailData

type PrDetailData struct {
	Page                string
	RepoName            string
	Branch              string
	Pr                  PrData
	Patchset            *Patchset
	FormattedPatchsetID string
	PatchsetDate        string
	Patches             []PatchSummary
	Patch               *PatchData
	PrevUrl             string
	NextUrl             string
	Logs                []EventLogData
	MetaData
}

type PrListData

type PrListData struct {
	PRs []PrListItem
	MetaData
}

type PrListItem

type PrListItem struct {
	ID            int64
	Name          string
	RepoName      string
	Status        Status
	FormattedDate string
	NumPatchsets  int
}

type RangeDiffFile

type RangeDiffFile struct {
	OldName string
	NewName string
	Type    string // "added", "removed", "changed"
}

RangeDiffFile represents a file-level change between two matched commits.

type RangeDiffHeader

type RangeDiffHeader struct {
	OldIdx         int
	OldSha         string
	OldAuthorName  string
	OldAuthorEmail string
	OldTitle       string
	OldBody        string
	NewIdx         int
	NewSha         string
	NewAuthorName  string
	NewAuthorEmail string
	NewTitle       string
	NewBody        string
	Title          string
	ContentEqual   bool
	AuthorChanged  bool
	TitleChanged   bool
	BodyChanged    bool
}

RangeDiffHeader is a header combining old and new commit pairs.

func NewRangeDiffHeader

func NewRangeDiffHeader(a, b *Patch, aIndex, bIndex int) *RangeDiffHeader

NewRangeDiffHeader creates a header from two patch ranges.

func (*RangeDiffHeader) String

func (hdr *RangeDiffHeader) String() string

type RangeDiffOutput

type RangeDiffOutput struct {
	Header *RangeDiffHeader
	Order  int
	Files  []*RangeDiffFile
	Type   string // "rm", "add", "equal", "changed"
}

RangeDiffOutput represents a single commit comparison entry in the range diff.

func RangeDiff

func RangeDiff(a []*Patch, b []*Patch) []*RangeDiffOutput

RangeDiff compares two patchsets and returns commit-level changes.

type RateLimiter

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

RateLimiter enforces a single global cap on submissions per interval, shared across all users (not keyed by pubkey or IP).

func NewRateLimiter

func NewRateLimiter(max int, interval time.Duration) *RateLimiter

func (*RateLimiter) Allow

func (r *RateLimiter) Allow() bool

Allow reports whether a new submission is permitted under the current window, incrementing the window's counter if so.

func (*RateLimiter) Error

func (r *RateLimiter) Error() error

type SemanticChange

type SemanticChange struct {
	Kind       SemanticChangeKind
	EntityKind string
	Name       string
	OldSig     string
	NewSig     string
	HunkIndex  int
	HunkAnchor string
}

SemanticChange is a single reviewer-facing summary line describing what changed about one entity in one hunk.

func AnalyzeSemanticChanges

func AnalyzeSemanticChanges(file *gitdiff.File) []SemanticChange

AnalyzeSemanticChanges produces a reviewer-facing list of semantic changes for a single diffed file. It only has access to the hunks present in the patch, not the full pre/post-image files, so entity extraction runs per-hunk on the old and new fragment text. Unsupported languages or parse failures degrade to an empty, non-error result so callers can always fall back to the line diff.

type SemanticChangeKind

type SemanticChangeKind string

SemanticChangeKind describes how an entity changed between the old and new side of a hunk.

const (
	SemanticAdded            SemanticChangeKind = "added"
	SemanticRemoved          SemanticChangeKind = "removed"
	SemanticModified         SemanticChangeKind = "modified"
	SemanticSignatureChanged SemanticChangeKind = "signature_changed"
	SemanticRenamed          SemanticChangeKind = "renamed"
)

type SemanticSummary

type SemanticSummary struct {
	Added             int
	Modified          int
	SignatureChanged  int
	Removed           int
	AnalyzedFileCount int
	SkippedFiles      []string
}

SemanticSummary aggregates semantic changes across every file in a patch, for a reviewer-facing rollup shown above the per-file breakdown.

func SummarizeSemanticChanges

func SummarizeSemanticChanges(summary SemanticSummary, fileName string, supported bool, changes []SemanticChange) SemanticSummary

SummarizeSemanticChanges folds one file's changes into a running summary. Call once per file in a patch with its changes (possibly nil) and whether the file's language was supported, then use the returned summary as-is.

func (SemanticSummary) HasContent

func (s SemanticSummary) HasContent() bool

func (SemanticSummary) Total

func (s SemanticSummary) Total() int

type Status

type Status string
const (
	StatusDraft Status = "draft"
	StatusOpen  Status = "open"
)

type TabStatus

type TabStatus string
const (
	TabStatusDraft    TabStatus = "draft"
	TabStatusActive   TabStatus = "active"
	TabStatusInactive TabStatus = "inactive"
)

type User

type User struct {
	ID        int64     `db:"id"`
	Pubkey    string    `db:"pubkey"`
	Name      string    `db:"name"`
	CreatedAt time.Time `db:"created_at"`
	UpdatedAt time.Time `db:"updated_at"`
}

User is a db model for users.

type UserData

type UserData struct {
	UserID    int64
	Name      string
	IsAdmin   bool
	Pubkey    string
	CreatedAt string
}

type WebCtx

type WebCtx struct {
	Pr        *PrCmd
	Backend   *Backend
	Formatter *formatterHtml.Formatter
	Logger    *slog.Logger
	Theme     *chroma.Style
}

Directories

Path Synopsis
cmd
patchbin command
contrib
dev command

Jump to

Keyboard shortcuts

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