نسخ من khaledmahfouz5/Maqtaa
Add listen to Unix websocket (#484)
هذا الالتزام موجود في:
@@ -43,6 +43,7 @@ sqlite.journal-mode: WAL
|
|||||||
|
|
||||||
# HTTP server configuration
|
# HTTP server configuration
|
||||||
# Host to bind to. Default: 0.0.0.0
|
# Host to bind to. Default: 0.0.0.0
|
||||||
|
# Use an IP address for network binding. Use a path for Unix socket binding (e.g. /run/opengist.sock)
|
||||||
http.host: 0.0.0.0
|
http.host: 0.0.0.0
|
||||||
|
|
||||||
# Port to bind to. Default: 6157
|
# Port to bind to. Default: 6157
|
||||||
@@ -51,6 +52,9 @@ http.port: 6157
|
|||||||
# Enable or disable git operations (clone, pull, push) via HTTP (either `true` or `false`). Default: true
|
# Enable or disable git operations (clone, pull, push) via HTTP (either `true` or `false`). Default: true
|
||||||
http.git-enabled: true
|
http.git-enabled: true
|
||||||
|
|
||||||
|
# File permissions for Unix socket (octal format). Default: 0666
|
||||||
|
unix-socket-permissions: 0666
|
||||||
|
|
||||||
# Enable or disable the metrics endpoint (either `true` or `false`). Default: false
|
# Enable or disable the metrics endpoint (either `true` or `false`). Default: false
|
||||||
metrics.enabled: false
|
metrics.enabled: false
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ aside: false
|
|||||||
# Configuration Cheat Sheet
|
# Configuration Cheat Sheet
|
||||||
|
|
||||||
| YAML Config Key | Environment Variable | Default value | Description |
|
| YAML Config Key | Environment Variable | Default value | Description |
|
||||||
|-----------------------|-------------------------------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-------------------------|-------------------------------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| log-level | OG_LOG_LEVEL | `warn` | Set the log level to one of the following: `debug`, `info`, `warn`, `error`, `fatal`. |
|
| log-level | OG_LOG_LEVEL | `warn` | Set the log level to one of the following: `debug`, `info`, `warn`, `error`, `fatal`. |
|
||||||
| log-output | OG_LOG_OUTPUT | `stdout,file` | Set the log output to one or more of the following: `stdout`, `file`. |
|
| log-output | OG_LOG_OUTPUT | `stdout,file` | Set the log output to one or more of the following: `stdout`, `file`. |
|
||||||
| external-url | OG_EXTERNAL_URL | none | Public URL to access to Opengist. |
|
| external-url | OG_EXTERNAL_URL | none | Public URL to access to Opengist. |
|
||||||
@@ -17,9 +17,10 @@ aside: false
|
|||||||
| index.meili.api-key | OG_MEILI_API_KEY | none | Set the API key for the Meiliseach server. |
|
| index.meili.api-key | OG_MEILI_API_KEY | none | Set the API key for the Meiliseach server. |
|
||||||
| git.default-branch | OG_GIT_DEFAULT_BRANCH | none | Default branch name used by Opengist when initializing Git repositories. If not set, uses the Git default branch name. More info [here](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup#_new_default_branch) |
|
| git.default-branch | OG_GIT_DEFAULT_BRANCH | none | Default branch name used by Opengist when initializing Git repositories. If not set, uses the Git default branch name. More info [here](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup#_new_default_branch) |
|
||||||
| sqlite.journal-mode | OG_SQLITE_JOURNAL_MODE | `WAL` | Set the journal mode for SQLite. More info [here](https://www.sqlite.org/pragma.html#pragma_journal_mode) |
|
| sqlite.journal-mode | OG_SQLITE_JOURNAL_MODE | `WAL` | Set the journal mode for SQLite. More info [here](https://www.sqlite.org/pragma.html#pragma_journal_mode) |
|
||||||
| http.host | OG_HTTP_HOST | `0.0.0.0` | The host on which the HTTP server should bind. |
|
| http.host | OG_HTTP_HOST | `0.0.0.0` | The host on which the HTTP server should bind. Use an IP address for network binding. Use a path for Unix socket binding (e.g. /run/opengist.sock) |
|
||||||
| http.port | OG_HTTP_PORT | `6157` | The port on which the HTTP server should listen. |
|
| http.port | OG_HTTP_PORT | `6157` | The port on which the HTTP server should listen. |
|
||||||
| http.git-enabled | OG_HTTP_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via HTTP. (`true` or `false`) |
|
| http.git-enabled | OG_HTTP_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via HTTP. (`true` or `false`) |
|
||||||
|
| unix-socket-permissions | OG_UNIX_SOCKET_PERMISSIONS | `0666` | File permissions for Unix socket (octal format). |
|
||||||
| metrics.enabled | OG_METRICS_ENABLED | `false` | Enable or disable Prometheus metrics endpoint at `/metrics` (`true` or `false`) |
|
| metrics.enabled | OG_METRICS_ENABLED | `false` | Enable or disable Prometheus metrics endpoint at `/metrics` (`true` or `false`) |
|
||||||
| ssh.git-enabled | OG_SSH_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via SSH. (`true` or `false`) |
|
| ssh.git-enabled | OG_SSH_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via SSH. (`true` or `false`) |
|
||||||
| ssh.host | OG_SSH_HOST | `0.0.0.0` | The host on which the SSH server should bind. |
|
| ssh.host | OG_SSH_HOST | `0.0.0.0` | The host on which the SSH server should bind. |
|
||||||
|
|||||||
@@ -36,11 +36,12 @@ var CmdStart = cli.Command{
|
|||||||
|
|
||||||
Initialize(ctx)
|
Initialize(ctx)
|
||||||
|
|
||||||
go server.NewServer(os.Getenv("OG_DEV") == "1", path.Join(config.GetHomeDir(), "sessions"), false).Start()
|
server := server.NewServer(os.Getenv("OG_DEV") == "1", path.Join(config.GetHomeDir(), "sessions"), false)
|
||||||
|
go server.Start()
|
||||||
go ssh.Start()
|
go ssh.Start()
|
||||||
|
|
||||||
<-stopCtx.Done()
|
<-stopCtx.Done()
|
||||||
shutdown()
|
shutdown(server)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -130,7 +131,7 @@ func Initialize(ctx *cli.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func shutdown() {
|
func shutdown(server *server.Server) {
|
||||||
log.Info().Msg("Shutting down database...")
|
log.Info().Msg("Shutting down database...")
|
||||||
if err := db.Close(); err != nil {
|
if err := db.Close(); err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to close database")
|
log.Error().Err(err).Msg("Failed to close database")
|
||||||
@@ -141,6 +142,8 @@ func shutdown() {
|
|||||||
index.Close()
|
index.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server.Stop()
|
||||||
|
|
||||||
log.Info().Msg("Shutdown complete")
|
log.Info().Msg("Shutdown complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ type config struct {
|
|||||||
HttpPort string `yaml:"http.port" env:"OG_HTTP_PORT"`
|
HttpPort string `yaml:"http.port" env:"OG_HTTP_PORT"`
|
||||||
HttpGit bool `yaml:"http.git-enabled" env:"OG_HTTP_GIT_ENABLED"`
|
HttpGit bool `yaml:"http.git-enabled" env:"OG_HTTP_GIT_ENABLED"`
|
||||||
|
|
||||||
|
UnixSocketPermissions string `yaml:"unix-socket-permissions" env:"OG_UNIX_SOCKET_PERMISSIONS"`
|
||||||
|
|
||||||
SshGit bool `yaml:"ssh.git-enabled" env:"OG_SSH_GIT_ENABLED"`
|
SshGit bool `yaml:"ssh.git-enabled" env:"OG_SSH_GIT_ENABLED"`
|
||||||
SshHost string `yaml:"ssh.host" env:"OG_SSH_HOST"`
|
SshHost string `yaml:"ssh.host" env:"OG_SSH_HOST"`
|
||||||
SshPort string `yaml:"ssh.port" env:"OG_SSH_PORT"`
|
SshPort string `yaml:"ssh.port" env:"OG_SSH_PORT"`
|
||||||
@@ -113,6 +115,8 @@ func configWithDefaults() (*config, error) {
|
|||||||
c.HttpPort = "6157"
|
c.HttpPort = "6157"
|
||||||
c.HttpGit = true
|
c.HttpGit = true
|
||||||
|
|
||||||
|
c.UnixSocketPermissions = "0666"
|
||||||
|
|
||||||
c.SshGit = true
|
c.SshGit = true
|
||||||
c.SshHost = "0.0.0.0"
|
c.SshHost = "0.0.0.0"
|
||||||
c.SshPort = "2222"
|
c.SshPort = "2222"
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/thomiceli/opengist/internal/validator"
|
"github.com/thomiceli/opengist/internal/validator"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -45,7 +51,19 @@ func NewServer(isDev bool, sessionsPath string, ignoreCsrf bool) *Server {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSocketPath(host string) bool {
|
||||||
|
return strings.Contains(host, "/") || strings.Contains(host, "\\")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) Start() {
|
func (s *Server) Start() {
|
||||||
|
if isSocketPath(config.C.HttpHost) {
|
||||||
|
s.startUnixSocket()
|
||||||
|
} else {
|
||||||
|
s.startHTTP()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) startHTTP() {
|
||||||
addr := config.C.HttpHost + ":" + config.C.HttpPort
|
addr := config.C.HttpHost + ":" + config.C.HttpPort
|
||||||
|
|
||||||
log.Info().Msg("Starting HTTP server on http://" + addr)
|
log.Info().Msg("Starting HTTP server on http://" + addr)
|
||||||
@@ -54,12 +72,106 @@ func (s *Server) Start() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) startUnixSocket() {
|
||||||
|
socketPath := config.C.HttpHost
|
||||||
|
if socketPath == "" {
|
||||||
|
socketPath = "/tmp/opengist.sock"
|
||||||
|
}
|
||||||
|
|
||||||
|
if dir := filepath.Dir(socketPath); dir != "." {
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
log.Warn().Err(err).Str("dir", dir).Msg("Failed to create socket directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
|
||||||
|
log.Warn().Err(err).Str("socket", socketPath).Msg("Failed to remove existing socket file")
|
||||||
|
}
|
||||||
|
|
||||||
|
pidPath := strings.TrimSuffix(socketPath, filepath.Ext(socketPath)) + ".pid"
|
||||||
|
if err := s.createPidFile(pidPath); err != nil {
|
||||||
|
log.Warn().Err(err).Str("pid-file", pidPath).Msg("Failed to create PID file")
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := net.Listen("unix", socketPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to start Unix socket server")
|
||||||
|
}
|
||||||
|
s.echo.Listener = listener
|
||||||
|
|
||||||
|
if config.C.UnixSocketPermissions != "" {
|
||||||
|
if perm, err := strconv.ParseUint(config.C.UnixSocketPermissions, 8, 32); err == nil {
|
||||||
|
if err := os.Chmod(socketPath, os.FileMode(perm)); err != nil {
|
||||||
|
log.Warn().Err(err).Str("socket", socketPath).Str("permissions", config.C.UnixSocketPermissions).Msg("Failed to set socket permissions")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warn().Err(err).Str("permissions", config.C.UnixSocketPermissions).Msg("Invalid socket permissions format")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Str("socket", socketPath).Msg("Starting Unix socket server")
|
||||||
|
log.Info().Str("pid-file", pidPath).Msg("PID file created")
|
||||||
|
server := new(http.Server)
|
||||||
|
if err := s.echo.StartServer(server); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to start Unix socket server")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) Stop() {
|
func (s *Server) Stop() {
|
||||||
|
if isSocketPath(config.C.HttpHost) {
|
||||||
|
s.stopUnixSocket()
|
||||||
|
} else {
|
||||||
|
s.stopHTTP()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) stopHTTP() {
|
||||||
|
log.Info().Msg("Stopping HTTP server...")
|
||||||
if err := s.echo.Close(); err != nil {
|
if err := s.echo.Close(); err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to stop HTTP server")
|
log.Fatal().Err(err).Msg("Failed to stop HTTP server")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) stopUnixSocket() {
|
||||||
|
log.Info().Msg("Stopping Unix socket server...")
|
||||||
|
|
||||||
|
var socketPath string
|
||||||
|
if s.echo.Listener != nil {
|
||||||
|
if unixListener, ok := s.echo.Listener.(*net.UnixListener); ok {
|
||||||
|
socketPath = unixListener.Addr().String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.echo.Close(); err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to stop Unix socket server")
|
||||||
|
}
|
||||||
|
|
||||||
|
if socketPath != "" {
|
||||||
|
if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
|
||||||
|
log.Error().Err(err).Str("socket", socketPath).Msg("Failed to remove socket file")
|
||||||
|
} else {
|
||||||
|
log.Info().Str("socket", socketPath).Msg("Socket file removed")
|
||||||
|
}
|
||||||
|
|
||||||
|
pidPath := strings.TrimSuffix(socketPath, filepath.Ext(socketPath)) + ".pid"
|
||||||
|
if err := os.Remove(pidPath); err != nil && !os.IsNotExist(err) {
|
||||||
|
log.Error().Err(err).Str("pid-file", pidPath).Msg("Failed to remove PID file")
|
||||||
|
} else {
|
||||||
|
log.Info().Str("pid-file", pidPath).Msg("PID file removed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) createPidFile(pidPath string) error {
|
||||||
|
pid := os.Getpid()
|
||||||
|
pidContent := fmt.Sprintf("%d\n", pid)
|
||||||
|
|
||||||
|
if err := os.WriteFile(pidPath, []byte(pidContent), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
s.echo.ServeHTTP(w, r)
|
s.echo.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,23 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Start background processes
|
||||||
make watch_frontend &
|
make watch_frontend &
|
||||||
make watch_backend &
|
FRONTEND_PID=$!
|
||||||
|
|
||||||
trap 'kill $(jobs -p)' EXIT
|
make watch_backend &
|
||||||
|
BACKEND_PID=$!
|
||||||
|
|
||||||
|
# Function for graceful shutdown
|
||||||
|
cleanup() {
|
||||||
|
echo "Shutting down gracefully..."
|
||||||
|
kill -TERM $FRONTEND_PID $BACKEND_PID 2>/dev/null || true
|
||||||
|
wait $FRONTEND_PID $BACKEND_PID 2>/dev/null || true
|
||||||
|
echo "Shutdown complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up trap for graceful shutdown
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
# Wait for background processes
|
||||||
wait
|
wait
|
||||||
المرجع في مشكلة جديدة
حظر مستخدم