Code cleanup. Use go mime type detection
This commit is contained in:
+4
-2
@@ -3,7 +3,6 @@ FROM golang:1.26-alpine AS builder
|
|||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
COPY go.mod ./
|
COPY go.mod ./
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
COPY index.html.tmpl main.go ./
|
COPY index.html.tmpl main.go ./
|
||||||
|
|
||||||
@@ -11,7 +10,10 @@ RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o beam .
|
|||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
RUN adduser -D -u 1000 beam
|
RUN apk add mailcap \ # for mime types
|
||||||
|
&& adduser -D -u 1000 beam \
|
||||||
|
&& mkdir /uploads \
|
||||||
|
&& chown beam /uploads
|
||||||
|
|
||||||
USER beam
|
USER beam
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
@@ -4,21 +4,24 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"html/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var uploadDir string
|
var uploadDir string
|
||||||
|
|
||||||
const idLength = 8
|
const idLength = 8
|
||||||
const idCharset = "abcdefghijklmnopqrstuvwxyz0123456789"
|
const idCharset = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
|
||||||
var indexHTML *template.Template
|
var indexHTML *template.Template
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
uploadDir = os.Getenv("UPLOAD_DIR")
|
uploadDir = os.Getenv("UPLOAD_DIR")
|
||||||
if uploadDir == "" {
|
if uploadDir == "" {
|
||||||
@@ -60,7 +63,7 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r.Method == "PUT" {
|
if r.Method == "PUT" {
|
||||||
handleCurlUpload(w, r, start)
|
handleUpload(w, r, start)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,11 +72,15 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.Method == "HEAD" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
logRequest(r, 405, time.Since(start))
|
logRequest(r, 405, time.Since(start))
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleCurlUpload(w http.ResponseWriter, r *http.Request, start time.Time) {
|
func handleUpload(w http.ResponseWriter, r *http.Request, start time.Time) {
|
||||||
filename := strings.TrimPrefix(r.URL.Path, "/")
|
filename := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
http.Error(w, "Filename required", http.StatusBadRequest)
|
http.Error(w, "Filename required", http.StatusBadRequest)
|
||||||
@@ -192,6 +199,12 @@ func handleDownload(w http.ResponseWriter, r *http.Request, start time.Time) {
|
|||||||
logRequest(r, 404, time.Since(start))
|
logRequest(r, 404, time.Since(start))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !strings.HasPrefix(filePath, uploadDir) {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
logRequest(r, 404, time.Since(start))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
stat, err := file.Stat()
|
stat, err := file.Stat()
|
||||||
@@ -212,7 +225,7 @@ func handleDownload(w http.ResponseWriter, r *http.Request, start time.Time) {
|
|||||||
func generateUniqueID() (string, error) {
|
func generateUniqueID() (string, error) {
|
||||||
const maxRetries = 100
|
const maxRetries = 100
|
||||||
|
|
||||||
for i := 0; i < maxRetries; i++ {
|
for range maxRetries {
|
||||||
id := generateID()
|
id := generateID()
|
||||||
targetDir := filepath.Join(uploadDir, id)
|
targetDir := filepath.Join(uploadDir, id)
|
||||||
|
|
||||||
@@ -249,11 +262,11 @@ func getHost(r *http.Request) string {
|
|||||||
// Check standard Forwarded header (RFC 7239)
|
// Check standard Forwarded header (RFC 7239)
|
||||||
if forwarded := r.Header.Get("Forwarded"); forwarded != "" {
|
if forwarded := r.Header.Get("Forwarded"); forwarded != "" {
|
||||||
// Parse "Forwarded: for=...; host=example.com; proto=https"
|
// Parse "Forwarded: for=...; host=example.com; proto=https"
|
||||||
parts := strings.Split(forwarded, ";")
|
parts := strings.SplitSeq(forwarded, ";")
|
||||||
for _, part := range parts {
|
for part := range parts {
|
||||||
part = strings.TrimSpace(part)
|
part = strings.TrimSpace(part)
|
||||||
if strings.HasPrefix(part, "host=") {
|
if host, found := strings.CutPrefix(part, "host="); found {
|
||||||
return strings.TrimPrefix(part, "host=")
|
return host
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,29 +280,23 @@ func getHost(r *http.Request) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getProtocol(r *http.Request) string {
|
func getProtocol(r *http.Request) string {
|
||||||
// Check X-Forwarded-Proto (common proxy header)
|
|
||||||
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
|
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
|
||||||
return proto
|
return proto
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check standard Forwarded header (RFC 7239)
|
|
||||||
if forwarded := r.Header.Get("Forwarded"); forwarded != "" {
|
if forwarded := r.Header.Get("Forwarded"); forwarded != "" {
|
||||||
// Parse "Forwarded: for=...; proto=https; host=..."
|
parts := strings.SplitSeq(forwarded, ";")
|
||||||
parts := strings.Split(forwarded, ";")
|
for part := range parts {
|
||||||
for _, part := range parts {
|
|
||||||
part = strings.TrimSpace(part)
|
part = strings.TrimSpace(part)
|
||||||
if strings.HasPrefix(part, "proto=") {
|
if proto, found := strings.CutPrefix(part, "proto="); found {
|
||||||
return strings.TrimPrefix(part, "proto=")
|
return proto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check X-Forwarded-SSL (some proxies use this)
|
|
||||||
if ssl := r.Header.Get("X-Forwarded-SSL"); ssl == "on" {
|
if ssl := r.Header.Get("X-Forwarded-SSL"); ssl == "on" {
|
||||||
return "https"
|
return "https"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if connection is TLS
|
|
||||||
if r.TLS != nil {
|
if r.TLS != nil {
|
||||||
return "https"
|
return "https"
|
||||||
}
|
}
|
||||||
@@ -298,28 +305,10 @@ func getProtocol(r *http.Request) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func detectContentType(filename string) string {
|
func detectContentType(filename string) string {
|
||||||
ext := strings.ToLower(filepath.Ext(filename))
|
ext := filepath.Ext(filename)
|
||||||
types := map[string]string{
|
mimeType := mime.TypeByExtension(ext)
|
||||||
".txt": "text/plain",
|
if mimeType == "" {
|
||||||
".html": "text/html",
|
return mimeType
|
||||||
".css": "text/css",
|
|
||||||
".js": "application/javascript",
|
|
||||||
".json": "application/json",
|
|
||||||
".xml": "application/xml",
|
|
||||||
".pdf": "application/pdf",
|
|
||||||
".zip": "application/zip",
|
|
||||||
".jpg": "image/jpeg",
|
|
||||||
".jpeg": "image/jpeg",
|
|
||||||
".png": "image/png",
|
|
||||||
".gif": "image/gif",
|
|
||||||
".svg": "image/svg+xml",
|
|
||||||
".mp3": "audio/mpeg",
|
|
||||||
".mp4": "video/mp4",
|
|
||||||
".webm": "video/webm",
|
|
||||||
}
|
|
||||||
|
|
||||||
if ct, ok := types[ext]; ok {
|
|
||||||
return ct
|
|
||||||
}
|
}
|
||||||
return "application/octet-stream"
|
return "application/octet-stream"
|
||||||
}
|
}
|
||||||
@@ -331,7 +320,7 @@ func logRequest(r *http.Request, status int, duration time.Duration) {
|
|||||||
func serveFrontpage(w http.ResponseWriter, r *http.Request) {
|
func serveFrontpage(w http.ResponseWriter, r *http.Request) {
|
||||||
proto := getProtocol(r)
|
proto := getProtocol(r)
|
||||||
host := getHost(r)
|
host := getHost(r)
|
||||||
pageData := map[string]string {"Proto": proto, "Host":host}
|
pageData := map[string]string{"Proto": proto, "Host": host}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
indexHTML.Execute(w, pageData)
|
indexHTML.Execute(w, pageData)
|
||||||
|
|||||||
Reference in New Issue
Block a user