package main
import (
"embed"
"encoding/json"
"flag"
"fmt"
"golang.org/x/time/rate"
"io"
"math/rand"
"mime"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
)
var (
domain string
listenAddr string
staticDir string
expireDur time.Duration
expireOnView bool
limiters = make(map[string]*rate.Limiter)
limMu sync.Mutex
useHTTPS bool
)
//go:embed android-chrome-192x192.png android-chrome-512x512.png apple-touch-icon.png favicon-16x16.png favicon-32x32.png favicon.ico site.webmanifest
var assetsFS embed.FS
type meta struct {
Expiry time.Time `json:"expiry"`
ExpireOnView bool `json:"expire_on_view"`
FileName string `json:"file_name,omitempty"`
}
func init() {
_ = mime.AddExtensionType(".webmanifest", "application/manifest+json")
}
func getLimiter(ip string) *rate.Limiter {
limMu.Lock()
defer limMu.Unlock()
lim, ok := limiters[ip]
if !ok {
lim = rate.NewLimiter(rate.Every(5*time.Second), 1)
limiters[ip] = lim
}
return lim
}
func rateLimitMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
if i := strings.LastIndex(ip, ":"); i != -1 {
ip = ip[:i]
}
lim := getLimiter(ip)
if !lim.Allow() {
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
return
}
next(w, r)
}
}
func randomID(n int) string {
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html; charset=utf-8")
scheme := "http"
if useHTTPS {
scheme = "https"
}
d := fmt.Sprintf("%s://%s", scheme, domain)
fmt.Fprintf(w, `
slenpaste
slenpaste
Welcome!
Upload a file:
curl -F 'file=@yourfile.txt' -F 'expiry=1h' %s/
Upload from stdin (no file param, expire after 5m):
curl --data-binary @- %s/?expiry=5m < yourfile.txt
Upload from stdin and expire on first view:
cat yourfile.txt | curl --data-binary @- "%s/?expiry=view"