Raw
 1package config
 2
 3import (
 4	"errors"
 5	"fmt"
 6	"io/fs"
 7	"os"
 8	"path/filepath"
 9	"strings"
10)
11
12const AppsDir = "apps.d"
13
14const maxAppsFile = 1 << 20
15
16func readAppsDir(configPath string, cfg *Config) []Problem {
17	dir := filepath.Join(filepath.Dir(configPath), AppsDir)
18	entries, err := os.ReadDir(dir)
19	if errors.Is(err, fs.ErrNotExist) {
20		return nil
21	}
22	if err != nil {
23		return []Problem{{File: dir, Reason: reasonUnreadable}}
24	}
25	used := map[string]bool{cfg.Server.DomainIDP: true, cfg.Server.DomainAccount: true, cfg.Server.DomainAdmin: true}
26	for _, a := range cfg.Apps {
27		used[a.Domain] = true
28	}
29	var problems []Problem
30	for _, e := range entries {
31		if e.IsDir() || !strings.HasSuffix(e.Name(), ".toml") {
32			continue
33		}
34		file := filepath.Join(dir, e.Name())
35		if cfg.Source == sourceGit {
36			problems = append(problems, Problem{File: file, Reason: reasonAppsUnderGit})
37			continue
38		}
39		data, reason := readRegular(file, maxAppsFile, reasonAppsTooLarge, nil)
40		if reason != "" {
41			problems = append(problems, Problem{File: file, Reason: reason})
42			continue
43		}
44		idx, keyProblems := indexKeys(file, data)
45		problems = append(problems, keyProblems...)
46		if idx == nil {
47			continue
48		}
49		others := map[string]bool{}
50		for path := range idx.present {
51			if top, _, _ := strings.Cut(path, "."); !strings.HasPrefix(top, "app[") && top != "app" && !others[top] {
52				problems = append(problems, Problem{File: file, Line: idx.line(top), Key: top, Reason: reasonAppsOnly})
53				others[top] = true
54			}
55		}
56		raw, typed := decodeTyped(file, data, idx, keyProblems)
57		problems = append(problems, typed...)
58		if raw == nil || len(others) > 0 || len(keyProblems) > 0 {
59			continue
60		}
61		v := &validator{file: file, idx: idx}
62		for i, a := range raw.App {
63			prefix := fmt.Sprintf("app[%d]", i)
64			app := v.app(prefix, a, cfg.Session.TTL)
65			if a.Domain != "" && used[a.Domain] {
66				v.add(prefix+".domain", reasonDomainReused)
67			}
68			used[a.Domain] = true
69			cfg.Apps = append(cfg.Apps, app)
70		}
71		problems = append(problems, v.problems...)
72	}
73	return problems
74}