1
0

webserv.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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 ws.isRunning {
  79. if err := ws.Stop(); err != nil {
  80. return err
  81. }
  82. }
  83. ws.option.Port = port
  84. ws.server.Addr = ":" + port
  85. err := ws.Start()
  86. if err != nil {
  87. return err
  88. }
  89. ws.option.Sysdb.Write("webserv", "port", port)
  90. return nil
  91. }
  92. // Start starts the web server.
  93. func (ws *WebServer) Start() error {
  94. ws.mu.Lock()
  95. defer ws.mu.Unlock()
  96. //Check if server already running
  97. if ws.isRunning {
  98. return fmt.Errorf("web server is already running")
  99. }
  100. //Check if the port is usable
  101. if IsPortInUse(ws.option.Port) {
  102. return errors.New("Port already in use or access denied by host OS")
  103. }
  104. //Dispose the old mux and create a new one
  105. ws.mux = http.NewServeMux()
  106. //Create a static web server
  107. fs := http.FileServer(http.Dir(filepath.Join(ws.option.WebRoot, "html")))
  108. ws.mux.Handle("/", ws.fsMiddleware(fs))
  109. ws.server = &http.Server{
  110. Addr: ":" + ws.option.Port,
  111. Handler: ws.mux,
  112. }
  113. go func() {
  114. if err := ws.server.ListenAndServe(); err != nil {
  115. if err != http.ErrServerClosed {
  116. fmt.Printf("Web server error: %v\n", err)
  117. }
  118. }
  119. }()
  120. log.Println("Static Web Server started. Listeing on :" + ws.option.Port)
  121. ws.isRunning = true
  122. ws.option.Sysdb.Write("webserv", "enabled", true)
  123. return nil
  124. }
  125. // Stop stops the web server.
  126. func (ws *WebServer) Stop() error {
  127. ws.mu.Lock()
  128. defer ws.mu.Unlock()
  129. if !ws.isRunning {
  130. return fmt.Errorf("web server is not running")
  131. }
  132. if err := ws.server.Close(); err != nil {
  133. return err
  134. }
  135. ws.isRunning = false
  136. ws.option.Sysdb.Write("webserv", "enabled", false)
  137. return nil
  138. }
  139. // UpdateDirectoryListing enables or disables directory listing.
  140. func (ws *WebServer) UpdateDirectoryListing(enable bool) {
  141. ws.option.EnableDirectoryListing = enable
  142. ws.option.Sysdb.Write("webserv", "dirlist", enable)
  143. }
  144. // Close stops the web server without returning an error.
  145. func (ws *WebServer) Close() {
  146. ws.Stop()
  147. }