175 lines
3.8 KiB
Go
175 lines
3.8 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/rpc"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/cors"
|
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/config"
|
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/dbutils"
|
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/handlers"
|
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/internal"
|
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/logging"
|
|
middlewares "github.com/marcopeocchi/yt-dlp-web-ui/server/middleware"
|
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/rest"
|
|
ytdlpRPC "github.com/marcopeocchi/yt-dlp-web-ui/server/rpc"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
type serverConfig struct {
|
|
frontend fs.FS
|
|
logger *slog.Logger
|
|
host string
|
|
port int
|
|
mdb *internal.MemoryDB
|
|
db *sql.DB
|
|
mq *internal.MessageQueue
|
|
}
|
|
|
|
func RunBlocking(host string, port int, frontend fs.FS, dbPath string) {
|
|
var mdb internal.MemoryDB
|
|
|
|
logger := slog.New(
|
|
slog.NewTextHandler(
|
|
io.MultiWriter(os.Stdout, logging.NewObservableLogger()),
|
|
&slog.HandlerOptions{},
|
|
),
|
|
)
|
|
|
|
mdb.Restore(logger)
|
|
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
logger.Error("failed to open database", slog.String("err", err.Error()))
|
|
}
|
|
|
|
err = dbutils.AutoMigrate(context.Background(), db)
|
|
if err != nil {
|
|
logger.Error("failed to init database", slog.String("err", err.Error()))
|
|
}
|
|
|
|
mq := internal.NewMessageQueue()
|
|
go mq.Subscriber()
|
|
|
|
srv := newServer(serverConfig{
|
|
frontend: frontend,
|
|
logger: logger,
|
|
host: host,
|
|
port: port,
|
|
mdb: &mdb,
|
|
mq: mq,
|
|
db: db,
|
|
})
|
|
|
|
go gracefulShutdown(srv, &mdb)
|
|
go autoPersist(time.Minute*5, &mdb, logger)
|
|
|
|
logger.Info("yt-dlp-webui started", slog.Int("port", port))
|
|
|
|
if err := srv.ListenAndServe(); err != nil {
|
|
logger.Warn("http server stopped", slog.String("err", err.Error()))
|
|
}
|
|
}
|
|
|
|
func newServer(c serverConfig) *http.Server {
|
|
service := ytdlpRPC.Container(c.mdb, c.mq, c.logger)
|
|
rpc.Register(service)
|
|
|
|
r := chi.NewRouter()
|
|
|
|
corsMiddleware := cors.New(cors.Options{
|
|
AllowedOrigins: []string{"*"},
|
|
AllowedMethods: []string{
|
|
http.MethodHead,
|
|
http.MethodGet,
|
|
http.MethodPost,
|
|
http.MethodPut,
|
|
http.MethodPatch,
|
|
http.MethodDelete,
|
|
},
|
|
AllowedHeaders: []string{"*"},
|
|
AllowCredentials: true,
|
|
})
|
|
|
|
r.Use(corsMiddleware.Handler)
|
|
// use in dev
|
|
// r.Use(middleware.Logger)
|
|
|
|
r.Mount("/", http.FileServer(http.FS(c.frontend)))
|
|
|
|
// Archive routes
|
|
r.Route("/archive", func(r chi.Router) {
|
|
if config.Instance().RequireAuth {
|
|
r.Use(middlewares.Authenticated)
|
|
}
|
|
r.Post("/downloaded", handlers.ListDownloaded)
|
|
r.Post("/delete", handlers.DeleteFile)
|
|
r.Get("/d/{id}", handlers.DownloadFile)
|
|
r.Get("/v/{id}", handlers.SendFile)
|
|
})
|
|
|
|
// Authentication routes
|
|
r.Route("/auth", func(r chi.Router) {
|
|
r.Post("/login", handlers.Login)
|
|
r.Get("/logout", handlers.Logout)
|
|
})
|
|
|
|
// RPC handlers
|
|
r.Route("/rpc", ytdlpRPC.ApplyRouter())
|
|
|
|
// REST API handlers
|
|
r.Route("/api/v1", rest.ApplyRouter(c.db, c.mdb, c.mq))
|
|
|
|
// Logging
|
|
r.Route("/log", logging.ApplyRouter())
|
|
|
|
return &http.Server{
|
|
Addr: fmt.Sprintf("%s:%d", c.host, c.port),
|
|
Handler: r,
|
|
}
|
|
}
|
|
|
|
func gracefulShutdown(srv *http.Server, db *internal.MemoryDB) {
|
|
ctx, stop := signal.NotifyContext(context.Background(),
|
|
os.Interrupt,
|
|
syscall.SIGTERM,
|
|
syscall.SIGQUIT,
|
|
)
|
|
|
|
go func() {
|
|
<-ctx.Done()
|
|
slog.Info("shutdown signal received")
|
|
|
|
defer func() {
|
|
db.Persist()
|
|
stop()
|
|
srv.Shutdown(context.TODO())
|
|
}()
|
|
}()
|
|
}
|
|
|
|
func autoPersist(d time.Duration, db *internal.MemoryDB, logger *slog.Logger) {
|
|
for {
|
|
if err := db.Persist(); err != nil {
|
|
logger.Info(
|
|
"failed to persisted session",
|
|
slog.String("err", err.Error()),
|
|
)
|
|
}
|
|
logger.Info("sucessfully persisted session")
|
|
time.Sleep(d)
|
|
}
|
|
}
|