github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/frontend/frontend.go (about)

     1  // Copyright 2020 Readium Foundation. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license
     3  // that can be found in the LICENSE file exposed on Github (readium) in the project repository.
     4  
     5  package main
     6  
     7  import (
     8  	"database/sql"
     9  	"fmt"
    10  	"log"
    11  	"os"
    12  	"os/signal"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strconv"
    16  	"strings"
    17  	"syscall"
    18  
    19  	auth "github.com/abbot/go-http-auth"
    20  	_ "github.com/go-sql-driver/mysql"
    21  	_ "github.com/lib/pq"
    22  	_ "github.com/mattn/go-sqlite3"
    23  	_ "github.com/microsoft/go-mssqldb"
    24  
    25  	"github.com/readium/readium-lcp-server/config"
    26  	frontend "github.com/readium/readium-lcp-server/frontend/server"
    27  	"github.com/readium/readium-lcp-server/frontend/webdashboard"
    28  	"github.com/readium/readium-lcp-server/frontend/weblicense"
    29  	"github.com/readium/readium-lcp-server/frontend/webpublication"
    30  	"github.com/readium/readium-lcp-server/frontend/webpurchase"
    31  	"github.com/readium/readium-lcp-server/frontend/webrepository"
    32  	"github.com/readium/readium-lcp-server/frontend/webuser"
    33  )
    34  
    35  func main() {
    36  	var static, configFile string
    37  	var err error
    38  
    39  	if configFile = os.Getenv("READIUM_FRONTEND_CONFIG"); configFile == "" {
    40  		configFile = "config.yaml"
    41  	}
    42  	config.ReadConfig(configFile)
    43  	log.Println("Config from " + configFile)
    44  
    45  	err = config.SetPublicUrls()
    46  	if err != nil {
    47  		panic(err)
    48  	}
    49  
    50  	//log.Println("LCP server = " + config.Config.LcpServer.PublicBaseUrl)
    51  	//log.Println("using login  " + config.Config.LcpUpdateAuth.Username)
    52  
    53  	driver, cnxn := config.GetDatabase(config.Config.FrontendServer.Database)
    54  	log.Println("Database driver " + driver)
    55  
    56  	db, err := sql.Open(driver, cnxn)
    57  	if err != nil {
    58  		panic(err)
    59  	}
    60  	if driver == "sqlite3" && !strings.Contains(cnxn, "_journal") {
    61  		_, err = db.Exec("PRAGMA journal_mode = WAL")
    62  		if err != nil {
    63  			panic(err)
    64  		}
    65  	}
    66  
    67  	repoManager, err := webrepository.Init(config.Config.FrontendServer)
    68  	if err != nil {
    69  		panic(err)
    70  	}
    71  
    72  	publicationDB, err := webpublication.Init(db)
    73  	if err != nil {
    74  		panic(err)
    75  	}
    76  
    77  	userDB, err := webuser.Open(db)
    78  	if err != nil {
    79  		panic(err)
    80  	}
    81  
    82  	purchaseDB, err := webpurchase.Init(db)
    83  	if err != nil {
    84  		panic(err)
    85  	}
    86  
    87  	dashboardDB, err := webdashboard.Init(db)
    88  	if err != nil {
    89  		panic(err)
    90  	}
    91  
    92  	licenseDB, err := weblicense.Init(db)
    93  	if err != nil {
    94  		panic(err)
    95  	}
    96  
    97  	static = config.Config.FrontendServer.Directory
    98  	if static == "" {
    99  		_, file, _, _ := runtime.Caller(0)
   100  		here := filepath.Dir(file)
   101  		static = filepath.Join(here, "../frontend/manage")
   102  	}
   103  
   104  	filepathConfigJs := filepath.Join(static, "config.js")
   105  	fileConfigJs, err := os.Create(filepathConfigJs)
   106  	if err != nil {
   107  		panic(err)
   108  	}
   109  
   110  	defer func() {
   111  		if err := fileConfigJs.Close(); err != nil {
   112  			panic(err)
   113  		}
   114  	}()
   115  
   116  	configJs := `
   117  	// This file is automatically generated, and git-ignored.
   118  	// To ignore your local changes, use:
   119  	// git update-index --assume-unchanged frontend/manage/config.js
   120  	window.Config = {`
   121  	configJs += "\n\tfrontend: {url: '" + config.Config.FrontendServer.PublicBaseUrl + "' },\n"
   122  	configJs += "\tlcp: {url: '" + config.Config.LcpServer.PublicBaseUrl + "', user: '" + config.Config.LcpUpdateAuth.Username + "', password: '" + config.Config.LcpUpdateAuth.Password + "'},\n"
   123  	configJs += "\tlsd: {url: '" + config.Config.LsdServer.PublicBaseUrl + "', user: '" + config.Config.LsdNotifyAuth.Username + "', password: '" + config.Config.LsdNotifyAuth.Password + "'}\n}"
   124  
   125  	// log.Println("manage/index.html config.js:")
   126  	// log.Println(configJs)
   127  
   128  	fileConfigJs.WriteString(configJs)
   129  	HandleSignals()
   130  
   131  	// basic authentication, optional in the frontend server.
   132  	// Authentication is used for getting user info from a license id.
   133  	var authenticator *auth.BasicAuth
   134  	authFile := config.Config.LsdServer.AuthFile
   135  	if authFile != "" {
   136  		_, err = os.Stat(authFile)
   137  		if err != nil {
   138  			panic(err)
   139  		}
   140  		htpasswd := auth.HtpasswdFileProvider(authFile)
   141  		authenticator = auth.NewBasicAuthenticator("Basic Realm", htpasswd)
   142  	}
   143  
   144  	s := frontend.New(":"+strconv.Itoa(config.Config.FrontendServer.Port), static, repoManager, publicationDB, userDB, dashboardDB, licenseDB, purchaseDB, authenticator)
   145  	log.Println("Frontend webserver for LCP running on " + config.Config.FrontendServer.Host + ":" + strconv.Itoa(config.Config.FrontendServer.Port))
   146  
   147  	if err := s.ListenAndServe(); err != nil {
   148  		log.Println("Error " + err.Error())
   149  	}
   150  }
   151  
   152  // HandleSignals handles system signals and adds a log before quitting
   153  func HandleSignals() {
   154  	sigChan := make(chan os.Signal)
   155  	go func() {
   156  		stacktrace := make([]byte, 1<<20)
   157  		for sig := range sigChan {
   158  			switch sig {
   159  			case syscall.SIGQUIT:
   160  				length := runtime.Stack(stacktrace, true)
   161  				fmt.Println(string(stacktrace[:length]))
   162  			case syscall.SIGINT:
   163  				fallthrough
   164  			case syscall.SIGTERM:
   165  				fmt.Println("Shutting down...")
   166  				os.Exit(0)
   167  			}
   168  		}
   169  	}()
   170  	signal.Notify(sigChan, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM)
   171  }