You've already forked hastebin-ansi
mirror of
https://github.com/armbian/hastebin-ansi.git
synced 2026-01-06 12:30:55 -08:00
166 lines
4.7 KiB
Go
166 lines
4.7 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/armbian/ansi-hastebin/internal/keygenerator"
|
|
"github.com/armbian/ansi-hastebin/internal/storage"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var (
|
|
pasteCreated = promauto.NewCounter(prometheus.CounterOpts{
|
|
Name: "hastebin_paste_created",
|
|
Help: "The total number of pastes created",
|
|
})
|
|
|
|
pasteRead = promauto.NewCounter(prometheus.CounterOpts{
|
|
Name: "hastebin_paste_read",
|
|
Help: "The total number of pastes read",
|
|
})
|
|
)
|
|
|
|
// DocumentHandler manages document operations
|
|
type DocumentHandler struct {
|
|
KeyLength int
|
|
MaxLength int
|
|
Store storage.Storage
|
|
KeyGenerator keygenerator.KeyGenerator
|
|
}
|
|
|
|
func NewDocumentHandler(keyLength, maxLength int, store storage.Storage, keyGenerator keygenerator.KeyGenerator) *DocumentHandler {
|
|
return &DocumentHandler{
|
|
KeyLength: keyLength,
|
|
MaxLength: maxLength,
|
|
Store: store,
|
|
KeyGenerator: keyGenerator,
|
|
}
|
|
}
|
|
|
|
// RegisterRoutes registers document routes
|
|
func (h *DocumentHandler) RegisterRoutes(r chi.Router) {
|
|
r.Get("/raw/{id}", h.HandleRawGet)
|
|
r.Head("/raw/{id}", h.HandleRawGet)
|
|
|
|
r.Post("/log", h.HandlePutLog)
|
|
r.Put("/log", h.HandlePutLog)
|
|
|
|
r.Post("/documents", h.HandlePost)
|
|
|
|
r.Get("/documents/{id}", h.HandleGet)
|
|
r.Head("/documents/{id}", h.HandleGet)
|
|
}
|
|
|
|
// Handle retrieving a document
|
|
func (h *DocumentHandler) HandleGet(w http.ResponseWriter, r *http.Request) {
|
|
key := strings.Split(chi.URLParam(r, "id"), ".")[0]
|
|
data, err := h.Store.Get(key, false)
|
|
|
|
if data != "" && err == nil {
|
|
log.Info().Str("key", key).Msg("Retrieved document")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if r.Method == http.MethodHead {
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
pasteRead.Inc()
|
|
json.NewEncoder(w).Encode(map[string]string{"data": data, "key": key})
|
|
} else {
|
|
log.Info().Str("key", key).Msg("Document not found")
|
|
http.Error(w, `{"message": "Document not found."}`, http.StatusNotFound)
|
|
}
|
|
}
|
|
|
|
// Handle retrieving raw document
|
|
func (h *DocumentHandler) HandleRawGet(w http.ResponseWriter, r *http.Request) {
|
|
key := strings.Split(chi.URLParam(r, "id"), ".")[0]
|
|
data, err := h.Store.Get(key, false)
|
|
|
|
if data != "" && err == nil {
|
|
log.Info().Str("key", key).Msg("Retrieved raw document")
|
|
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
|
|
if r.Method == http.MethodHead {
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
pasteRead.Inc()
|
|
w.Write([]byte(data))
|
|
} else {
|
|
log.Info().Str("key", key).Msg("Raw document not found")
|
|
http.Error(w, `{"message": "Document not found."}`, http.StatusNotFound)
|
|
}
|
|
}
|
|
|
|
// Handle adding a new document (POST)
|
|
func (h *DocumentHandler) HandlePost(w http.ResponseWriter, r *http.Request) {
|
|
var buffer strings.Builder
|
|
if err := h.readBody(r, &buffer); err != nil {
|
|
http.Error(w, `{"message": "Error reading request body."}`, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if h.MaxLength > 0 && buffer.Len() > h.MaxLength {
|
|
log.Info().Str("key", "").Msg("Document exceeds max length")
|
|
http.Error(w, `{"message": "Document exceeds maximum length."}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
key := h.KeyGenerator.Generate(h.KeyLength)
|
|
h.Store.Set(key, buffer.String(), false)
|
|
|
|
log.Info().Str("key", key).Msg("Added document")
|
|
|
|
pasteCreated.Inc()
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]string{"key": key})
|
|
}
|
|
|
|
// Handle PUT request that returns a direct link
|
|
func (h *DocumentHandler) HandlePutLog(w http.ResponseWriter, r *http.Request) {
|
|
var buffer strings.Builder
|
|
if err := h.readBody(r, &buffer); err != nil {
|
|
http.Error(w, `{"message": "Error reading request body."}`, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if h.MaxLength > 0 && buffer.Len() > h.MaxLength {
|
|
log.Info().Str("key", "").Msg("Document exceeds max length")
|
|
http.Error(w, `{"message": "Document exceeds maximum length."}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
key := h.KeyGenerator.Generate(h.KeyLength)
|
|
h.Store.Set(key, buffer.String(), false)
|
|
|
|
log.Info().Str("key", key).Msg("Added document with log link")
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
fmt.Fprintf(w, "\nhttps://%s/%s\n\n", r.Host, key)
|
|
}
|
|
|
|
// Reads body from the request
|
|
func (h *DocumentHandler) readBody(r *http.Request, buffer *strings.Builder) error {
|
|
if strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") {
|
|
r.ParseMultipartForm(32 << 20)
|
|
if val := r.FormValue("data"); val != "" {
|
|
buffer.WriteString(val)
|
|
}
|
|
} else {
|
|
data, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Error reading request body")
|
|
return err
|
|
}
|
|
buffer.WriteString(string(data))
|
|
}
|
|
return nil
|
|
}
|