init ingestion

This commit is contained in:
2026-05-24 22:59:24 +07:00
commit 4e8c11d545
80 changed files with 5639 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
// Package apperr defines AppError, the single error type returned by every
// service/repo function. Handlers translate AppError into HTTP responses.
package apperr
import (
"errors"
"fmt"
"net/http"
)
type AppError struct {
Code int // HTTP status to return
Message string // user-facing message (safe to expose)
Field string // optional: which field caused the error
RetryAfter int // seconds, for 429
Err error // original error for logging (never exposed)
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %v", e.Message, e.Err)
}
return e.Message
}
func (e *AppError) Unwrap() error { return e.Err }
// As reports whether err is or wraps an *AppError.
func As(err error) (*AppError, bool) {
var ae *AppError
if errors.As(err, &ae) {
return ae, true
}
return nil, false
}
// ---------------------------------------------------------------------------
// Constructors
// ---------------------------------------------------------------------------
func BadRequest(msg, field string, err error) *AppError {
return &AppError{Code: http.StatusBadRequest, Message: msg, Field: field, Err: err}
}
func Unauthorized(msg string) *AppError {
return &AppError{Code: http.StatusUnauthorized, Message: msg}
}
func Forbidden(msg string) *AppError {
return &AppError{Code: http.StatusForbidden, Message: msg}
}
func NotFound(msg string) *AppError {
return &AppError{Code: http.StatusNotFound, Message: msg}
}
func Conflict(msg string, err error) *AppError {
return &AppError{Code: http.StatusConflict, Message: msg, Err: err}
}
func PayloadTooLarge(msg string) *AppError {
return &AppError{Code: http.StatusRequestEntityTooLarge, Message: msg}
}
func UnprocessableEntity(msg string) *AppError {
return &AppError{Code: http.StatusUnprocessableEntity, Message: msg}
}
func TooManyRequests(retryAfterSeconds int) *AppError {
return &AppError{
Code: http.StatusTooManyRequests,
Message: "rate limit exceeded",
RetryAfter: retryAfterSeconds,
}
}
func Internal(err error) *AppError {
return &AppError{Code: http.StatusInternalServerError, Message: "internal server error", Err: err}
}