api

package
v1.7.0 Latest Latest
Warning

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

Go to latest
Published: Mar 15, 2026 License: GPL-3.0 Imports: 28 Imported by: 0

Documentation

Overview

Package api provides the HTTP API handlers for minimaldoc-server.

Index

Constants

View Source
const (
	ErrBadRequest     = "BAD_REQUEST"
	ErrDatabaseError  = "DATABASE_ERROR"
	ErrInternalError  = "INTERNAL_ERROR"
	ErrUnauthorized   = "UNAUTHORIZED"
	ErrForbidden      = "FORBIDDEN"
	ErrNotFound       = "NOT_FOUND"
	ErrConflict       = "CONFLICT"
	ErrRateLimited    = "RATE_LIMITED"
	ErrNotAvailable   = "NOT_AVAILABLE"
	ErrMissingParams  = "MISSING_PARAMETERS"
	ErrInvalidRequest = "INVALID_REQUEST"
)

General

View Source
const (
	ErrInvalidCredentials     = "INVALID_CREDENTIALS"
	ErrInvalidBootstrapToken  = "INVALID_BOOTSTRAP_TOKEN"
	ErrInvalidRefreshToken    = "INVALID_REFRESH_TOKEN"
	ErrInvalidState           = "INVALID_STATE"
	ErrInvalidVerifyToken     = "INVALID_VERIFY_TOKEN"
	ErrTokenGenerationFailed  = "TOKEN_GENERATION_FAILED"
	ErrPasswordRequired       = "PASSWORD_REQUIRED"
	ErrPasswordTooShort       = "PASSWORD_TOO_SHORT"
	ErrPasswordHashFailed     = "PASSWORD_HASH_FAILED"
	ErrUserNotFound           = "USER_NOT_FOUND"
	ErrUserAlreadyExists      = "USER_ALREADY_EXISTS"
	ErrUserCreationFailed     = "USER_CREATION_FAILED"
	ErrEmailVerifyFailed      = "EMAIL_VERIFY_FAILED"
	ErrRegistrationRequired   = "REGISTRATION_REQUIRED"
	ErrUnknownProvider        = "UNKNOWN_PROVIDER"
	ErrOAuthLinkFailed        = "OAUTH_LINK_FAILED"
	ErrMissingSiteContext     = "MISSING_SITE_CONTEXT"
	ErrCannotModifySelf       = "CANNOT_MODIFY_SELF"
	ErrInsufficientPermission = "INSUFFICIENT_PERMISSION"
	ErrOwnPostsOnly           = "OWN_POSTS_ONLY"
	ErrBanned                 = "BANNED"
)

Authentication & Authorization

View Source
const (
	ErrSiteNotFound       = "SITE_NOT_FOUND"
	ErrSiteInvalid        = "SITE_INVALID"
	ErrSiteCreationFailed = "SITE_CREATION_FAILED"
	ErrSiteUpdateFailed   = "SITE_UPDATE_FAILED"
	ErrSiteDeleteFailed   = "SITE_DELETE_FAILED"
	ErrSiteIDRequired     = "SITE_ID_REQUIRED"
	ErrAPIKeyGenFailed    = "API_KEY_GENERATION_FAILED"
	ErrBootstrapDone      = "BOOTSTRAP_ALREADY_DONE"
)

Site

View Source
const (
	ErrSubscribeFailed   = "SUBSCRIBE_FAILED"
	ErrUnsubscribeFailed = "UNSUBSCRIBE_FAILED"
	ErrIDGenFailed       = "ID_GENERATION_FAILED"
)

Newsletter

View Source
const (
	ErrPostNotFound       = "POST_NOT_FOUND"
	ErrPostCreationFailed = "POST_CREATION_FAILED"
	ErrPostUpdateFailed   = "POST_UPDATE_FAILED"
	ErrPostDeleteFailed   = "POST_DELETE_FAILED"
	ErrPostPublishFailed  = "POST_PUBLISH_FAILED"
	ErrSlugExists         = "SLUG_EXISTS"
	ErrInvalidContent     = "INVALID_CONTENT"
	ErrInvalidDatetime    = "INVALID_DATETIME"
	ErrSchedulePastDate   = "SCHEDULE_PAST_DATE"
	ErrCommentFailed      = "COMMENT_FAILED"
	ErrDailyLimitReached  = "DAILY_LIMIT_REACHED"
	ErrAuthRequired       = "AUTHENTICATION_REQUIRED"
)

Blog

View Source
const (
	ErrTopicNotFound       = "TOPIC_NOT_FOUND"
	ErrTopicCreationFailed = "TOPIC_CREATION_FAILED"
	ErrTopicClosed         = "TOPIC_CLOSED"
	ErrCategoryNotFound    = "CATEGORY_NOT_FOUND"
	ErrTagNotFound         = "TAG_NOT_FOUND"
	ErrTagExists           = "TAG_EXISTS"
)

Forum

View Source
const (
	ErrDocNotFound    = "DOCUMENT_NOT_FOUND"
	ErrAccessDenied   = "ACCESS_DENIED"
	ErrInvalidAPIKey  = "INVALID_API_KEY"
	ErrInvalidPath    = "INVALID_PATH"
	ErrPathRequired   = "PATH_REQUIRED"
	ErrRuleNotFound   = "RULE_NOT_FOUND"
	ErrRuleExists     = "RULE_EXISTS"
	ErrRuleIDRequired = "RULE_ID_REQUIRED"
)

Document Access

View Source
const (
	ErrUploadFailed     = "UPLOAD_FAILED"
	ErrUploadNotFound   = "UPLOAD_NOT_FOUND"
	ErrNoFileProvided   = "NO_FILE_PROVIDED"
	ErrStorageNotConfig = "STORAGE_NOT_CONFIGURED"
	ErrNotUploadOwner   = "NOT_UPLOAD_OWNER"
)

Upload

View Source
const (
	ErrDBUnreachable = "DATABASE_UNREACHABLE"
)

Health

Variables

This section is empty.

Functions

func AdminMiddleware

func AdminMiddleware() gin.HandlerFunc

AdminMiddleware ensures the user has admin role.

func AdminUIAuthMiddleware

func AdminUIAuthMiddleware(cfg *config.Config) gin.HandlerFunc

AdminUIAuthMiddleware validates JWT for admin UI routes. Redirects to login page instead of returning JSON error.

func AuthMiddleware

func AuthMiddleware(cfg *config.Config) gin.HandlerFunc

AuthMiddleware validates JWT tokens or session cookies.

func AuthorOrAboveMiddleware

func AuthorOrAboveMiddleware() gin.HandlerFunc

AuthorOrAboveMiddleware requires admin, editor, or author role.

func CORSMiddleware

func CORSMiddleware(allowedOrigins []string) gin.HandlerFunc

CORSMiddleware handles Cross-Origin Resource Sharing.

func CanEditPostMiddleware

func CanEditPostMiddleware() gin.HandlerFunc

CanEditPostMiddleware checks if user can edit a specific post. Admin/editor can edit any post, author can only edit own posts. Requires post_id param and author_id to be fetched before this middleware.

func EditorOrAboveMiddleware

func EditorOrAboveMiddleware() gin.HandlerFunc

EditorOrAboveMiddleware requires admin or editor role.

func LoggerMiddleware

func LoggerMiddleware() gin.HandlerFunc

LoggerMiddleware logs HTTP requests.

func OptionalAuthMiddleware

func OptionalAuthMiddleware(cfg *config.Config) gin.HandlerFunc

OptionalAuthMiddleware tries to authenticate the user but doesn't require it. If a valid token is present, it sets user info in context. If not, it continues without setting user info (for anonymous access).

func SecurityHeadersMiddleware

func SecurityHeadersMiddleware() gin.HandlerFunc

SecurityHeadersMiddleware adds common security headers to responses.

func SiteMiddleware

func SiteMiddleware() gin.HandlerFunc

SiteMiddleware validates and sets site context from user's JWT. For API key-based requests, handlers validate the key directly.

func TracingMiddleware

func TracingMiddleware() gin.HandlerFunc

TracingMiddleware creates a span per HTTP request with method, path, and status attributes.

Types

type BlogCommentRequest

type BlogCommentRequest struct {
	AuthorName  string `json:"author_name" form:"author_name"`
	AuthorEmail string `json:"author_email" form:"author_email"`
	Content     string `json:"content" form:"content" binding:"required,min=1,max=5000"`
	ParentID    string `json:"parent_id" form:"parent_id"`
}

BlogCommentRequest represents a comment submission. AuthorName and AuthorEmail are optional for authenticated users (auto-filled from profile).

type BlogPostRequest

type BlogPostRequest struct {
	Slug          string `json:"slug" binding:"required"`
	Title         string `json:"title" binding:"required"`
	Description   string `json:"description"`
	Content       string `json:"content" binding:"required"`
	FeaturedImage string `json:"featured_image"`
	Tags          string `json:"tags"` // JSON array string
	Category      string `json:"category"`
	Visibility    string `json:"visibility"`   // public, authenticated, role_viewer, role_author, role_editor, role_admin
	ScheduledAt   string `json:"scheduled_at"` // ISO 8601 datetime for scheduled publishing
}

BlogPostRequest represents the request body for creating/updating a blog post.

type BlogPostResponse

type BlogPostResponse struct {
	ID            string `json:"id"`
	Slug          string `json:"slug"`
	Title         string `json:"title"`
	Description   string `json:"description,omitempty"`
	Content       string `json:"content,omitempty"`
	ContentHTML   string `json:"content_html,omitempty"`
	FeaturedImage string `json:"featured_image,omitempty"`
	Tags          string `json:"tags,omitempty"`
	Category      string `json:"category,omitempty"`
	Status        string `json:"status"`
	Visibility    string `json:"visibility"`
	PublishedAt   string `json:"published_at,omitempty"`
	ScheduledAt   string `json:"scheduled_at,omitempty"`
	CreatedAt     string `json:"created_at"`
	UpdatedAt     string `json:"updated_at"`
	AuthorName    string `json:"author_name,omitempty"`
	ReadingTime   int    `json:"reading_time,omitempty"`
}

BlogPostResponse is the JSON-friendly response for a blog post.

type BootstrapRequest

type BootstrapRequest struct {
	SiteName       string `json:"site_name" binding:"required"`
	Domain         string `json:"domain"`
	Email          string `json:"email" binding:"required,email"`
	Password       string `json:"password"`
	BootstrapToken string `json:"bootstrap_token"`
}

BootstrapRequest represents bootstrap parameters.

type BootstrapResult

type BootstrapResult struct {
	SiteID   string
	SiteName string
	APIKey   string
	UserID   string
	Email    string
	Password string
}

BootstrapResult contains the created site and user info.

func Bootstrap

func Bootstrap(ctx context.Context, db store.Store, cfg *config.Config, siteName, domain, adminEmail, adminPassword string) (*BootstrapResult, error)

Bootstrap creates the initial site and admin user. Returns the generated API key and credentials.

type CheckAccessRequest

type CheckAccessRequest struct {
	Path string `json:"path" form:"path" binding:"required"`
}

CheckAccessRequest is the request for checking doc access.

type CheckAccessResponse

type CheckAccessResponse struct {
	Path         string `json:"path"`
	IsProtected  bool   `json:"is_protected"`
	RequiredRole string `json:"required_role,omitempty"`
	HasAccess    bool   `json:"has_access"`
	Reason       string `json:"reason,omitempty"`
}

CheckAccessResponse is the response for access check.

type CreateDocAccessRequest

type CreateDocAccessRequest struct {
	PathPattern  string `json:"path_pattern" binding:"required"`
	RequiredRole string `json:"required_role" binding:"required,oneof=viewer author editor admin"`
	Description  string `json:"description"`
}

CreateDocAccessRequest is the request for creating a doc access rule.

type DocAccessRule

type DocAccessRule struct {
	ID           string `json:"id"`
	PathPattern  string `json:"path_pattern"`
	RequiredRole string `json:"required_role"`
	Description  string `json:"description,omitempty"`
	CreatedAt    string `json:"created_at"`
	UpdatedAt    string `json:"updated_at"`
}

DocAccessRule is the API representation of a doc access rule.

type DurationRequest

type DurationRequest struct {
	SiteID      string `json:"site_id" binding:"required,max=64"`
	Path        string `json:"path" binding:"required,max=512"`
	Duration    int    `json:"duration" binding:"required,min=1,max=3600"`
	SessionHash string `json:"session_hash" binding:"max=64"`
	IsBounce    *bool  `json:"is_bounce"`
}

DurationRequest represents a page duration update.

type EventRequest

type EventRequest struct {
	SiteID      string `json:"site_id" binding:"required,max=64"`
	Name        string `json:"name" binding:"required,max=128"`
	Category    string `json:"category" binding:"max=64"`
	Path        string `json:"path" binding:"max=512"`
	Value       string `json:"value" binding:"max=1024"`
	SessionHash string `json:"session_hash" binding:"max=64"`
}

EventRequest represents a custom event tracking request.

type FeedbackRequest

type FeedbackRequest struct {
	SiteID      string `json:"site_id" binding:"required"`
	Path        string `json:"path" binding:"required"`
	Rating      int    `json:"rating" binding:"required,min=1,max=5"`
	Feedback    string `json:"feedback"`
	SessionHash string `json:"session_hash"`
}

FeedbackRequest represents a feedback submission.

type ForumBanRequest

type ForumBanRequest struct {
	UserID      string `json:"user_id" binding:"required"`
	Reason      string `json:"reason" binding:"required"`
	ExpiresAt   string `json:"expires_at"`
	IsPermanent bool   `json:"is_permanent"`
}

type ForumCategoryRequest

type ForumCategoryRequest struct {
	Slug        string `json:"slug" binding:"required"`
	Name        string `json:"name" binding:"required"`
	Description string `json:"description"`
	Color       string `json:"color"`
	Icon        string `json:"icon"`
	Position    int    `json:"position"`
	ParentID    string `json:"parent_id"`
	IsLocked    bool   `json:"is_locked"`
}

type ForumCategoryResponse

type ForumCategoryResponse struct {
	ID          string                  `json:"id"`
	Slug        string                  `json:"slug"`
	Name        string                  `json:"name"`
	Description string                  `json:"description,omitempty"`
	Color       string                  `json:"color,omitempty"`
	Icon        string                  `json:"icon,omitempty"`
	Position    int                     `json:"position"`
	IsLocked    bool                    `json:"is_locked"`
	TopicCount  int64                   `json:"topic_count"`
	PostCount   int64                   `json:"post_count"`
	ParentID    string                  `json:"parent_id,omitempty"`
	Children    []ForumCategoryResponse `json:"children,omitempty"`
	CreatedAt   string                  `json:"created_at"`
}

type ForumFlagRequest

type ForumFlagRequest struct {
	TopicID     string `json:"topic_id"`
	PostID      string `json:"post_id"`
	Reason      string `json:"reason" binding:"required"`
	Description string `json:"description"`
}

type ForumPostRequest

type ForumPostRequest struct {
	Content  string `json:"content" binding:"required"`
	ParentID string `json:"parent_id"`
}

type ForumPostResponse

type ForumPostResponse struct {
	ID           string `json:"id"`
	TopicID      string `json:"topic_id"`
	Content      string `json:"content"`
	ContentHTML  string `json:"content_html"`
	LikeCount    int64  `json:"like_count"`
	IsSolution   bool   `json:"is_solution"`
	ParentID     string `json:"parent_id,omitempty"`
	AuthorID     string `json:"author_id,omitempty"`
	AuthorName   string `json:"author_name,omitempty"`
	AuthorAvatar string `json:"author_avatar,omitempty"`
	EditedAt     string `json:"edited_at,omitempty"`
	EditorName   string `json:"editor_name,omitempty"`
	CreatedAt    string `json:"created_at"`
	IsLiked      bool   `json:"is_liked,omitempty"`
}

type ForumTagRequest

type ForumTagRequest struct {
	Slug        string `json:"slug" binding:"required"`
	Name        string `json:"name" binding:"required"`
	Description string `json:"description"`
	Color       string `json:"color"`
}

type ForumTagResponse

type ForumTagResponse struct {
	ID          string `json:"id"`
	Slug        string `json:"slug"`
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Color       string `json:"color,omitempty"`
	TopicCount  int64  `json:"topic_count"`
}

type ForumTopicRequest

type ForumTopicRequest struct {
	Title      string `json:"title" binding:"required"`
	Content    string `json:"content" binding:"required"`
	CategoryID string `json:"category_id"`
	Tags       string `json:"tags"` // comma-separated
}

type ForumTopicResponse

type ForumTopicResponse struct {
	ID           string   `json:"id"`
	Slug         string   `json:"slug"`
	Title        string   `json:"title"`
	Content      string   `json:"content,omitempty"`
	ContentHTML  string   `json:"content_html,omitempty"`
	Status       string   `json:"status"`
	IsPinned     bool     `json:"is_pinned"`
	IsSolved     bool     `json:"is_solved"`
	ViewCount    int64    `json:"view_count"`
	LikeCount    int64    `json:"like_count"`
	PostCount    int64    `json:"post_count"`
	CategoryID   string   `json:"category_id,omitempty"`
	CategoryName string   `json:"category_name,omitempty"`
	CategorySlug string   `json:"category_slug,omitempty"`
	AuthorID     string   `json:"author_id,omitempty"`
	AuthorName   string   `json:"author_name,omitempty"`
	AuthorAvatar string   `json:"author_avatar,omitempty"`
	Tags         []string `json:"tags,omitempty"`
	LastPostAt   string   `json:"last_post_at,omitempty"`
	CreatedAt    string   `json:"created_at"`
	UpdatedAt    string   `json:"updated_at"`
	IsLiked      bool     `json:"is_liked,omitempty"`
	IsBookmarked bool     `json:"is_bookmarked,omitempty"`
}

type ForumUserStatsResponse

type ForumUserStatsResponse struct {
	UserID     string `json:"user_id"`
	UserName   string `json:"user_name"`
	UserAvatar string `json:"user_avatar,omitempty"`
	Reputation int64  `json:"reputation"`
	TopicCount int64  `json:"topic_count"`
	PostCount  int64  `json:"post_count"`
	LikesGiven int64  `json:"likes_given"`
	LikesRecv  int64  `json:"likes_received"`
}

type LoginRequest

type LoginRequest struct {
	Email    string `json:"email" binding:"required,email"`
	Password string `json:"password" binding:"required"`
	SiteID   string `json:"site_id" binding:"required"`
}

LoginRequest represents login credentials.

type RSSChannel

type RSSChannel struct {
	Title         string    `xml:"title"`
	Link          string    `xml:"link"`
	Description   string    `xml:"description"`
	Language      string    `xml:"language"`
	LastBuildDate string    `xml:"lastBuildDate"`
	Items         []RSSItem `xml:"item"`
}

type RSSFeed

type RSSFeed struct {
	XMLName xml.Name   `xml:"rss"`
	Version string     `xml:"version,attr"`
	Channel RSSChannel `xml:"channel"`
}

RSS Feed structures

type RSSItem

type RSSItem struct {
	Title       string `xml:"title"`
	Link        string `xml:"link"`
	Description string `xml:"description"`
	PubDate     string `xml:"pubDate"`
	GUID        string `xml:"guid"`
	Category    string `xml:"category,omitempty"`
}

type RegisterRequest

type RegisterRequest struct {
	Email    string `json:"email" binding:"required,email"`
	Password string `json:"password" binding:"required,min=8"`
	Name     string `json:"name"`
	SiteID   string `json:"site_id" binding:"required"`
}

RegisterRequest represents public registration parameters.

type Router

type Router struct {
	*gin.Engine
	// contains filtered or unexported fields
}

Router wraps the Gin engine with dependencies.

func NewAdminRouter

func NewAdminRouter(cfg *config.Config, db store.Store, emailSender email.Sender, store storage.Storage) *Router

NewAdminRouter creates the admin API and UI router. This handles: authentication, dashboard, management APIs. Should be on a separate port, not publicly accessible.

func NewPublicRouter

func NewPublicRouter(cfg *config.Config, db store.Store, emailSender email.Sender) *Router

NewPublicRouter creates the public-facing API router. This handles: tracking, feedback submission, newsletter subscribe. No authentication required, no admin access.

func (*Router) Stop

func (r *Router) Stop()

Stop gracefully stops background tasks like rate limiter cleanup.

type ScheduleRequest

type ScheduleRequest struct {
	ScheduledAt string `json:"scheduled_at" binding:"required"` // ISO 8601 datetime
}

ScheduleRequest represents the request to schedule a post.

type StatCard

type StatCard struct {
	Value     interface{}
	Label     string
	Secondary bool
}

StatCard represents a stat card for display.

type SubscribeRequest

type SubscribeRequest struct {
	SiteID string `json:"site_id" binding:"required"`
	Email  string `json:"email" binding:"required,email"`
}

SubscribeRequest represents a newsletter subscription request.

type TableColumn

type TableColumn struct {
	Header string
	Class  string
}

TableColumn defines a column in a data table.

type TableRow

type TableRow struct {
	Cells   []string
	Actions string // Optional action buttons HTML
}

TableRow represents a row of cells.

type TrackRequest

type TrackRequest struct {
	SiteID      string `json:"site_id" binding:"required,max=64"`
	Path        string `json:"path" binding:"required,max=512"`
	Referrer    string `json:"referrer" binding:"max=1024"`
	Country     string `json:"country" binding:"max=8"`
	DeviceType  string `json:"device_type" binding:"max=16"`
	Browser     string `json:"browser" binding:"max=64"`
	OS          string `json:"os" binding:"max=64"`
	SessionHash string `json:"session_hash" binding:"max=64"`
}

TrackRequest represents a page view tracking request.

type UpdateDocAccessRequest

type UpdateDocAccessRequest struct {
	PathPattern  string `json:"path_pattern" binding:"required"`
	RequiredRole string `json:"required_role" binding:"required,oneof=viewer author editor admin"`
	Description  string `json:"description"`
}

UpdateDocAccessRequest is the request for updating a doc access rule.

Jump to

Keyboard shortcuts

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