marky

Lightweight bookmarking + plaintext archive
Log | Files | Refs | README | LICENSE

marky.go (2262B)


      1 package main
      2 
      3 import (
      4 	"context"
      5 	"flag"
      6 	"fmt"
      7 	"log"
      8 	"net/http"
      9 	"os"
     10 	"os/signal"
     11 	"syscall"
     12 	"time"
     13 
     14 	dbpkg "git.sr.ht/~dce/marky/internal"
     15 )
     16 
     17 func main() {
     18 	// Resolve default DB path from internal package.
     19 	defaultDBPath, _ := dbpkg.DefaultPath()
     20 
     21 	// Usage/help
     22 	flag.Usage = func() {
     23 		fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options]\n\n", os.Args[0])
     24 		fmt.Fprintf(flag.CommandLine.Output(), "Options:\n")
     25 		flag.PrintDefaults()
     26 	}
     27 
     28 	// Flags
     29 	addr := flag.String("addr", ":8080", "listen address (e.g. :8080, 127.0.0.1:8080)")
     30 	dbPath := flag.String("db", defaultDBPath, "path to SQLite DB file (created if missing)")
     31 	disableSignup := flag.Bool("disable-signup", false, "disable signup endpoint and hide from UI")
     32 	baseURL := flag.String("base-url", "", "public base URL (e.g. https://bookmarks.example.com)")
     33 
     34 	// Also accept explicit --help to exit 0 with usage.
     35 	for _, a := range os.Args[1:] {
     36 		if a == "--help" {
     37 			flag.Usage()
     38 			return
     39 		}
     40 	}
     41 
     42 	flag.Parse()
     43 
     44 	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
     45 	defer stop()
     46 
     47 	// Open database (creates and migrates if needed)
     48 	db, err := dbpkg.OpenAtPath(ctx, *dbPath)
     49 	if err != nil {
     50 		log.Fatalf("open db: %v", err)
     51 	}
     52 	defer db.Close()
     53 
     54 	// Build the shared HTTP handler (API + UI)
     55 	h, err := dbpkg.NewHandlerWithConfig(db, dbpkg.HandlerConfig{
     56 		DisableSignup: *disableSignup,
     57 		BaseURL:       *baseURL,
     58 	})
     59 	if err != nil {
     60 		log.Fatalf("build handler: %v", err)
     61 	}
     62 
     63 	srv := &http.Server{
     64 		Addr:              *addr,
     65 		Handler:           logging(h),
     66 		ReadHeaderTimeout: 5 * time.Second,
     67 	}
     68 
     69 	go func() {
     70 		log.Printf("marky API listening on %s", srv.Addr)
     71 		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
     72 			log.Fatalf("server error: %v", err)
     73 		}
     74 	}()
     75 
     76 	<-ctx.Done()
     77 	shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
     78 	defer cancel()
     79 	if err := srv.Shutdown(shutdownCtx); err != nil {
     80 		log.Printf("server shutdown error: %v", err)
     81 	}
     82 }
     83 
     84 func logging(next http.Handler) http.Handler {
     85 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
     86 		start := time.Now()
     87 		next.ServeHTTP(w, r)
     88 		log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
     89 	})
     90 }