2023-07-22 00:59:45 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-08-10 13:28:30 +01:00
|
|
|
"context"
|
2023-11-17 09:09:11 +00:00
|
|
|
"errors"
|
2023-07-22 00:59:45 +01:00
|
|
|
"flag"
|
2024-08-10 13:28:30 +01:00
|
|
|
"github.com/1f349/bluebell/conf"
|
|
|
|
"github.com/1f349/bluebell/logger"
|
|
|
|
"github.com/1f349/bluebell/serve"
|
|
|
|
"github.com/1f349/bluebell/upload"
|
|
|
|
"github.com/charmbracelet/log"
|
|
|
|
"github.com/cloudflare/tableflip"
|
2023-07-22 00:59:45 +01:00
|
|
|
"github.com/julienschmidt/httprouter"
|
2023-08-21 00:27:54 +01:00
|
|
|
"github.com/spf13/afero"
|
2024-08-10 13:28:30 +01:00
|
|
|
"gopkg.in/yaml.v3"
|
2023-07-22 00:59:45 +01:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
2024-08-10 13:28:30 +01:00
|
|
|
"path/filepath"
|
2023-07-22 00:59:45 +01:00
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2024-08-10 13:28:30 +01:00
|
|
|
configPath = flag.String("conf", "", "Config file path")
|
|
|
|
debugLog = flag.Bool("debug", false, "Enable debug logging")
|
|
|
|
pidFile = flag.String("pid-file", "", "Path to pid file")
|
2023-07-22 00:59:45 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2023-11-17 09:09:11 +00:00
|
|
|
flag.Parse()
|
2024-08-10 13:28:30 +01:00
|
|
|
if *debugLog {
|
|
|
|
logger.Logger.SetLevel(log.DebugLevel)
|
|
|
|
}
|
|
|
|
logger.Logger.Info("Starting...")
|
2023-11-17 09:09:11 +00:00
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
upg, err := tableflip.New(tableflip.Options{
|
|
|
|
PIDFile: *pidFile,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2023-07-22 00:59:45 +01:00
|
|
|
}
|
2024-08-10 13:28:30 +01:00
|
|
|
defer upg.Stop()
|
|
|
|
|
|
|
|
if *configPath == "" {
|
|
|
|
logger.Logger.Error("Config flag is missing")
|
|
|
|
os.Exit(1)
|
2023-07-22 00:59:45 +01:00
|
|
|
}
|
2024-08-10 13:28:30 +01:00
|
|
|
|
|
|
|
openConf, err := os.Open(*configPath)
|
2023-07-22 00:59:45 +01:00
|
|
|
if err != nil {
|
2024-08-10 13:28:30 +01:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
logger.Logger.Error("Missing config file")
|
|
|
|
} else {
|
|
|
|
logger.Logger.Error("Open config file", "err", err)
|
|
|
|
}
|
|
|
|
os.Exit(1)
|
2023-07-22 00:59:45 +01:00
|
|
|
}
|
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
var config conf.Conf
|
|
|
|
err = yaml.NewDecoder(openConf).Decode(&config)
|
|
|
|
if err != nil {
|
|
|
|
logger.Logger.Error("Invalid config file", "err", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2023-07-22 00:59:45 +01:00
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
wd := filepath.Dir(*configPath)
|
|
|
|
sitesDir := filepath.Join(wd, "sites")
|
2023-07-22 00:59:45 +01:00
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
_, err = os.Stat(sitesDir)
|
|
|
|
if err != nil {
|
|
|
|
logger.Logger.Fatal("Failed to find sites, does the directory exist? Error: ", err)
|
2023-07-22 00:59:45 +01:00
|
|
|
}
|
2024-08-10 13:28:30 +01:00
|
|
|
|
|
|
|
sitesFs := afero.NewBasePathFs(afero.NewOsFs(), sitesDir)
|
|
|
|
|
|
|
|
// Do an upgrade on SIGHUP
|
2023-07-22 00:59:45 +01:00
|
|
|
go func() {
|
2024-08-10 13:28:30 +01:00
|
|
|
sig := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sig, syscall.SIGHUP)
|
|
|
|
for range sig {
|
|
|
|
err := upg.Upgrade()
|
|
|
|
if err != nil {
|
|
|
|
logger.Logger.Error("Failed upgrade", "err", err)
|
2023-07-22 00:59:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
// Listen must be called before Ready
|
|
|
|
ln, err := upg.Listen("tcp", config.Listen)
|
|
|
|
if err != nil {
|
|
|
|
logger.Logger.Fatal("Listen failed", "err", err)
|
|
|
|
}
|
2023-08-21 00:27:54 +01:00
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
uploadHandler := upload.New(sitesFs)
|
|
|
|
serveHandler := serve.New(sitesFs)
|
2023-08-21 00:27:54 +01:00
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
router := httprouter.New()
|
|
|
|
router.POST("/u/:site", uploadHandler.Handle)
|
|
|
|
router.GET("/*filepath", serveHandler.Handle)
|
|
|
|
|
|
|
|
server := &http.Server{
|
|
|
|
Handler: router,
|
|
|
|
ReadTimeout: 1 * time.Minute,
|
|
|
|
ReadHeaderTimeout: 1 * time.Minute,
|
|
|
|
WriteTimeout: 1 * time.Minute,
|
|
|
|
IdleTimeout: 1 * time.Minute,
|
|
|
|
MaxHeaderBytes: 4_096_000,
|
|
|
|
}
|
|
|
|
logger.Logger.Info("HTTP server listening on", "addr", config.Listen)
|
2023-08-21 00:27:54 +01:00
|
|
|
go func() {
|
2024-08-10 13:28:30 +01:00
|
|
|
err := server.Serve(ln)
|
|
|
|
if !errors.Is(err, http.ErrServerClosed) {
|
|
|
|
logger.Logger.Fatal("Serve failed", "err", err)
|
2023-08-21 00:27:54 +01:00
|
|
|
}
|
|
|
|
}()
|
2023-07-22 00:59:45 +01:00
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
logger.Logger.Info("Ready")
|
|
|
|
if err := upg.Ready(); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
<-upg.Exit()
|
2023-07-22 00:59:45 +01:00
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
time.AfterFunc(30*time.Second, func() {
|
|
|
|
logger.Logger.Warn("Graceful shutdown timed out")
|
|
|
|
os.Exit(1)
|
|
|
|
})
|
2023-07-22 00:59:45 +01:00
|
|
|
|
2024-08-10 13:28:30 +01:00
|
|
|
server.Shutdown(context.Background())
|
2023-07-22 00:59:45 +01:00
|
|
|
}
|