webserv.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package webserv
  2. import (
  3. "embed"
  4. _ "embed"
  5. "errors"
  6. "fmt"
  7. "log"
  8. "net/http"
  9. "os"
  10. "path/filepath"
  11. "sync"
  12. "imuslab.com/zoraxy/mod/database"
  13. "imuslab.com/zoraxy/mod/utils"
  14. )
  15. /*
  16. Static Web Server package
  17. This module host a static web server
  18. */
  19. //go:embed templates/*
  20. var templates embed.FS
  21. type WebServerOptions struct {
  22. Port string //Port for listening
  23. EnableDirectoryListing bool //Enable listing of directory
  24. WebRoot string //Folder for stroing the static web folders
  25. EnableWebDirManager bool //Enable web file manager to handle files in web directory
  26. Sysdb *database.Database //Database for storing configs
  27. }
  28. type WebServer struct {
  29. mux *http.ServeMux
  30. server *http.Server
  31. option *WebServerOptions
  32. isRunning bool
  33. mu sync.Mutex
  34. }
  35. // NewWebServer creates a new WebServer instance. One instance only
  36. func NewWebServer(options *WebServerOptions) *WebServer {
  37. if !utils.FileExists(options.WebRoot) {
  38. //Web root folder not exists. Create one with default templates
  39. os.MkdirAll(filepath.Join(options.WebRoot, "html"), 0775)
  40. os.MkdirAll(filepath.Join(options.WebRoot, "templates"), 0775)
  41. indexTemplate, err := templates.ReadFile("templates/index.html")
  42. if err != nil {
  43. log.Println("Failed to read static wev server template file: ", err.Error())
  44. } else {
  45. os.WriteFile(filepath.Join(options.WebRoot, "html", "index.html"), indexTemplate, 0775)
  46. }
  47. }
  48. //Create new table to store the config
  49. options.Sysdb.NewTable("webserv")
  50. return &WebServer{
  51. mux: http.NewServeMux(),
  52. option: options,
  53. isRunning: false,
  54. mu: sync.Mutex{},
  55. }
  56. }
  57. // Restore the configuration to previous config
  58. func (ws *WebServer) RestorePreviousState() {
  59. //Set the port
  60. port := ws.option.Port
  61. ws.option.Sysdb.Read("webserv", "port", &port)
  62. ws.option.Port = port
  63. //Set the enable directory list
  64. enableDirList := ws.option.EnableDirectoryListing
  65. ws.option.Sysdb.Read("webserv", "dirlist", &enableDirList)
  66. ws.option.EnableDirectoryListing = enableDirList
  67. //Check the running state
  68. webservRunning := false
  69. ws.option.Sysdb.Read("webserv", "enabled", &webservRunning)
  70. if webservRunning {
  71. ws.Start()
  72. } else {
  73. ws.Stop()
  74. }
  75. }
  76. // ChangePort changes the server's port.
  77. func (ws *WebServer) ChangePort(port string) error {
  78. if IsPortInUse(port) {
  79. return errors.New("Selected port is used by another process")
  80. }
  81. if ws.isRunning {
  82. if err := ws.Stop(); err != nil {
  83. return err
  84. }
  85. }
  86. ws.option.Port = port
  87. ws.server.Addr = ":" + port
  88. err := ws.Start()
  89. if err != nil {
  90. return err
  91. }
  92. ws.option.Sysdb.Write("webserv", "port", port)
  93. return nil
  94. }
  95. // Start starts the web server.
  96. func (ws *WebServer) Start() error {
  97. ws.mu.Lock()
  98. defer ws.mu.Unlock()
  99. //Check if server already running
  100. if ws.isRunning {
  101. return fmt.Errorf("web server is already running")
  102. }
  103. //Check if the port is usable
  104. if IsPortInUse(ws.option.Port) {
  105. return errors.New("Port already in use or access denied by host OS")
  106. }
  107. //Dispose the old mux and create a new one
  108. ws.mux = http.NewServeMux()
  109. //Create a static web server
  110. fs := http.FileServer(http.Dir(filepath.Join(ws.option.WebRoot, "html")))
  111. ws.mux.Handle("/", ws.fsMiddleware(fs))
  112. ws.server = &http.Server{
  113. Addr: ":" + ws.option.Port,
  114. Handler: ws.mux,
  115. }
  116. go func() {
  117. if err := ws.server.ListenAndServe(); err != nil {
  118. if err != http.ErrServerClosed {
  119. fmt.Printf("Web server error: %v\n", err)
  120. }
  121. }
  122. }()
  123. log.Println("Static Web Server started. Listeing on :" + ws.option.Port)
  124. ws.isRunning = true
  125. ws.option.Sysdb.Write("webserv", "enabled", true)
  126. return nil
  127. }
  128. // Stop stops the web server.
  129. func (ws *WebServer) Stop() error {
  130. ws.mu.Lock()
  131. defer ws.mu.Unlock()
  132. if !ws.isRunning {
  133. return fmt.Errorf("web server is not running")
  134. }
  135. if err := ws.server.Close(); err != nil {
  136. return err
  137. }
  138. ws.isRunning = false
  139. ws.option.Sysdb.Write("webserv", "enabled", false)
  140. return nil
  141. }
  142. // UpdateDirectoryListing enables or disables directory listing.
  143. func (ws *WebServer) UpdateDirectoryListing(enable bool) {
  144. ws.option.EnableDirectoryListing = enable
  145. ws.option.Sysdb.Write("webserv", "dirlist", enable)
  146. }
  147. // Close stops the web server without returning an error.
  148. func (ws *WebServer) Close() {
  149. ws.Stop()
  150. }