Skip to content

Commit

Permalink
Added default domain config option
Browse files Browse the repository at this point in the history
  • Loading branch information
akclace committed Nov 17, 2024
1 parent eaa6cce commit 49f36eb
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 27 deletions.
33 changes: 16 additions & 17 deletions internal/server/app_apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,36 +508,35 @@ func (s *Server) verifyClientCerts(r *http.Request, authName string) error {

func (s *Server) MatchApp(hostHeader, matchPath string) (types.AppInfo, error) {
//s.Trace().Msgf("MatchApp %s %s", hostHeader, matchPath)
apps, err := s.apps.GetAllApps()
apps, domainMap, err := s.apps.GetAppInfo()
if err != nil {
return types.AppInfo{}, err
}
matchPath = normalizePath(matchPath)

// Find unique domains
domainMap := map[string]bool{}
for _, appInfo := range apps {
if !domainMap[appInfo.Domain] {
domainMap[appInfo.Domain] = true
// TODO : cache domain list
}
}

// Check if host header matches a known domain
checkDomain := false
if hostHeader != "" && domainMap[hostHeader] {
checkDomain = true
}

for _, appInfo := range apps {
if checkDomain && appInfo.Domain != hostHeader {
// Request uses known domain, but app is not for this domain
continue
}
if s.config.System.DisableUnknownDomains {
appDomain := cmp.Or(appInfo.Domain, s.config.System.DefaultDomain)
if hostHeader != appDomain {
// Host header does not match
continue
}
} else {
if checkDomain && appInfo.Domain != hostHeader {
// Request uses known domain, but app is not for this domain
continue
}

if !checkDomain && appInfo.Domain != "" {
// Request does not use known domain, but app is for a domain
continue
if !checkDomain && appInfo.Domain != "" {
// Request does not use known domain, but app is for a domain
continue
}
}

if strings.HasPrefix(matchPath, appInfo.Path) {
Expand Down
76 changes: 69 additions & 7 deletions internal/server/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import (
"github.com/claceio/clace/internal/types"
)

// AppStore is a store of apps. Apps are initialized lazily, the first GetApp call on each app
// will load the app from the database.
// AppStore is a store of apps. List of apps is stored in memory. Apps are initialized lazily,
// AddApp has to be called before GetApp to initialize the app
type AppStore struct {
*types.Logger
server *Server
allApps []types.AppInfo
server *Server
allApps []types.AppInfo
allDomains map[string]bool

mu sync.RWMutex
appMap map[types.AppPathDomain]*app.App
Expand All @@ -31,28 +32,86 @@ func NewAppStore(logger *types.Logger, server *Server) *AppStore {
}
}

func (a *AppStore) GetAllApps() ([]types.AppInfo, error) {
func (a *AppStore) GetAppInfo() ([]types.AppInfo, map[string]bool, error) {
a.mu.RLock()
if a.allApps != nil {
a.mu.RUnlock()
return a.allApps, a.allDomains, nil
}
a.mu.RUnlock()

// Get exclusive lock
a.mu.Lock()
defer a.mu.Unlock()

err := a.updateAppInfo(a.allApps)
if err != nil {
return nil, nil, err
}
return a.allApps, a.allDomains, nil
}

func (a *AppStore) GetAllApps() ([]types.AppInfo, error) {
a.mu.RLock()
if a.allApps != nil {
a.mu.RUnlock()
return a.allApps, nil
}
a.mu.RUnlock()

// Get exclusive lock
a.mu.Lock()
defer a.mu.Unlock()

err := a.updateAppInfo(a.allApps)
if err != nil {
return nil, err
}
return a.allApps, nil
}

func (a *AppStore) GetAllDomains() (map[string]bool, error) {
a.mu.RLock()
if a.allDomains != nil {
a.mu.RUnlock()
return a.allDomains, nil
}
a.mu.RUnlock()

// Get exclusive lock
a.mu.Lock()
defer a.mu.Unlock()

err := a.updateAppInfo(a.allApps)
if err != nil {
return nil, err
}
return a.allDomains, nil
}

func (a *AppStore) updateAppInfo(allApps []types.AppInfo) error {
var err error
a.allApps, err = a.server.db.GetAllApps(true)
if err != nil {
return nil, err
return err
}

return a.allApps, nil
a.allDomains = make(map[string]bool)
a.allDomains[a.server.config.System.DefaultDomain] = true
for _, appInfo := range allApps {
if appInfo.Domain != "" {
a.allDomains[appInfo.Domain] = true
}
}
return nil
}

func (a *AppStore) ClearAllAppCache() {
a.mu.Lock()
defer a.mu.Unlock()

a.allApps = nil
a.allDomains = nil
}

func (a *AppStore) GetApp(pathDomain types.AppPathDomain) (*app.App, error) {
Expand Down Expand Up @@ -87,6 +146,7 @@ func (a *AppStore) DeleteLinkedApps(pathDomain types.AppPathDomain) error {

a.clearApp(pathDomain)
a.allApps = nil
a.allDomains = nil
return nil
}

Expand All @@ -106,6 +166,7 @@ func (a *AppStore) DeleteApps(pathDomain []types.AppPathDomain) {
a.clearApp(pd)
}
a.allApps = nil
a.allDomains = nil
}

func (a *AppStore) UpdateApps(apps []*app.App) {
Expand All @@ -118,4 +179,5 @@ func (a *AppStore) UpdateApps(apps []*app.App) {
a.appMap[types.CreateAppPathDomain(app.Path, app.Domain)] = app
}
a.allApps = nil
a.allDomains = nil
}
15 changes: 13 additions & 2 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,19 @@ func (s *Server) setupHTTPSServer() (*http.Server, error) {
magicConfig := certmagic.NewDefault()
magicConfig.OnDemand = &certmagic.OnDemandConfig{
DecisionFunc: func(name string) error {
// Always issue a certificate
return nil
if !s.config.System.DisableUnknownDomains {
// Allow on-demand certificates for unknown domains
return nil
}

allDomains, err := s.apps.GetAllDomains()
if err != nil {
return err
}
if allDomains[name] {
return nil
}
return fmt.Errorf("unknown domain %s", name)
},
}
tlsConfig = magicConfig.TLSConfig()
Expand Down
2 changes: 2 additions & 0 deletions internal/system/clace.default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ tailwindcss_command = "tailwindcss"
file_watcher_debounce_millis = 300
node_path = "" # node module lookup paths https://esbuild.github.io/api/#node-paths
container_command = "auto" # "auto" or "docker" or "podman"
default_domain = "localhost" # default domain for apps
disable_unknown_domains = true # disable unknown domains, if default domain is set. Otherwise, unknown domains are allowed

[plugin."store.in"]
db_connection = "sqlite:$CL_HOME/clace_app.db"
Expand Down
2 changes: 2 additions & 0 deletions internal/system/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ func TestServerConfig(t *testing.T) {
testutil.AssertEqualsString(t, "tailwind command", "tailwindcss", c.System.TailwindCSSCommand)
testutil.AssertEqualsInt(t, "file debounce", 300, c.System.FileWatcherDebounceMillis)
testutil.AssertEqualsString(t, "node path", "", c.System.NodePath)
testutil.AssertEqualsString(t, "default domain", "localhost", c.System.DefaultDomain)
testutil.AssertEqualsBool(t, "unknown domains", true, c.System.DisableUnknownDomains)

// Global Settings
testutil.AssertEqualsString(t, "server uri", "$CL_HOME/run/clace.sock", c.ServerUri)
Expand Down
2 changes: 2 additions & 0 deletions internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ type SystemConfig struct {
FileWatcherDebounceMillis int `toml:"file_watcher_debounce_millis"`
NodePath string `toml:"node_path"`
ContainerCommand string `toml:"container_command"`
DefaultDomain string `toml:"default_domain"`
DisableUnknownDomains bool `toml:"disable_unknown_domains"`
}

// GitAuth is a github auth config entry
Expand Down
2 changes: 1 addition & 1 deletion tests/commander/test_load_app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ tests:
stdout: "Disallow: /"
exit-code: 0
load0070: # Update app code
command: perl -pi -e 's/Disk Usage/DiskTest Usage/g' ./disk_usage/app.star && sleep 3
command: perl -pi -e 's/Disk Usage/DiskTest Usage/g' ./disk_usage/app.star && sleep 4
load0080: # with --dev, changes are picked up immediately
command: curl -su "admin:qwerty" localhost:25222/disk_usage_dev
stderr:
Expand Down

0 comments on commit 49f36eb

Please sign in to comment.