forked from CWAD/relibre
Being suport for binary builds, AUR, Flatpak
This commit is contained in:
163
main.go
Normal file
163
main.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// main.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
webview "github.com/webview/webview_go"
|
||||
)
|
||||
|
||||
//go:embed index.html ui/* images/*
|
||||
var content embed.FS
|
||||
|
||||
type saveReq struct {
|
||||
Filename string `json:"filename"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type saveResp struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Static files
|
||||
subFS, err := fs.Sub(content, ".")
|
||||
if err != nil {
|
||||
log.Fatalf("fs.Sub error: %v", err)
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", http.FileServer(http.FS(subFS)))
|
||||
|
||||
// /save endpoint writes to Downloads
|
||||
mux.HandleFunc("/save", func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
defer func() { log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start)) }()
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var req saveReq
|
||||
if err := json.NewDecoder(io.LimitReader(r.Body, 10<<20)).Decode(&req); err != nil {
|
||||
http.Error(w, "bad json", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.Filename == "" {
|
||||
req.Filename = fmt.Sprintf("relibre-%d.html", time.Now().Unix())
|
||||
}
|
||||
req.Filename = sanitizeFilename(req.Filename)
|
||||
|
||||
dir := downloadsDir()
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
http.Error(w, "mkdir failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
path := filepath.Join(dir, req.Filename)
|
||||
if err := os.WriteFile(path, []byte(req.Content), 0o644); err != nil {
|
||||
http.Error(w, "write failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("Saved HTML -> %s", path)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(saveResp{Path: path})
|
||||
})
|
||||
|
||||
// Simple request logger
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
mux.ServeHTTP(w, r)
|
||||
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
|
||||
})
|
||||
|
||||
// Start server on ephemeral port
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
log.Fatalf("listen: %v", err)
|
||||
}
|
||||
addr := ln.Addr().String()
|
||||
srv := &http.Server{Handler: handler, ReadHeaderTimeout: 5 * time.Second}
|
||||
|
||||
go func() {
|
||||
log.Printf("HTTP listening on http://%s", addr)
|
||||
if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("http serve: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Native webview window (debug true for dev tools/log signals)
|
||||
debug := true
|
||||
w := webview.New(debug)
|
||||
defer w.Destroy()
|
||||
w.SetTitle("Relibre")
|
||||
w.SetSize(1100, 750, webview.HintNone)
|
||||
w.Navigate(fmt.Sprintf("http://%s", addr))
|
||||
w.Run()
|
||||
|
||||
_ = srv.Close()
|
||||
if runtime.GOOS == "linux" {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func sanitizeFilename(name string) string {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return "relibre.html"
|
||||
}
|
||||
if !strings.HasSuffix(strings.ToLower(name), ".html") {
|
||||
name += ".html"
|
||||
}
|
||||
re := regexp.MustCompile(`[<>:"/\\|?*\x00-\x1F]`)
|
||||
return re.ReplaceAllString(name, "_")
|
||||
}
|
||||
|
||||
func downloadsDir() string {
|
||||
if p := readXDGDownloads(); p != "" {
|
||||
return p
|
||||
}
|
||||
home, _ := os.UserHomeDir()
|
||||
if home == "" {
|
||||
return "./"
|
||||
}
|
||||
return filepath.Join(home, "Downloads")
|
||||
}
|
||||
|
||||
func readXDGDownloads() string {
|
||||
home, _ := os.UserHomeDir()
|
||||
if home == "" {
|
||||
return ""
|
||||
}
|
||||
path := filepath.Join(home, ".config", "user-dirs.dirs")
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer f.Close()
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
line := sc.Text()
|
||||
if strings.HasPrefix(line, "XDG_DOWNLOAD_DIR=") {
|
||||
raw := strings.TrimPrefix(line, "XDG_DOWNLOAD_DIR=")
|
||||
raw = strings.Trim(raw, `"`)
|
||||
raw = strings.ReplaceAll(raw, "$HOME", home)
|
||||
return raw
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user