configurators.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. package configurators
  2. import (
  3. "bufio"
  4. "encoding/hex"
  5. "fmt"
  6. "os"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "github.com/imgproxy/imgproxy/v3/imagetype"
  11. )
  12. type URLReplacement struct {
  13. Regexp *regexp.Regexp
  14. Replacement string
  15. }
  16. func Int(i *int, name string) {
  17. if env, err := strconv.Atoi(os.Getenv(name)); err == nil {
  18. *i = env
  19. }
  20. }
  21. func Float(i *float64, name string) {
  22. if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
  23. *i = env
  24. }
  25. }
  26. func MegaInt(f *int, name string) {
  27. if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
  28. *f = int(env * 1000000)
  29. }
  30. }
  31. func String(s *string, name string) {
  32. if env := os.Getenv(name); len(env) > 0 {
  33. *s = env
  34. }
  35. }
  36. func StringSliceSep(s *[]string, name, sep string) {
  37. if env := os.Getenv(name); len(env) > 0 {
  38. parts := strings.Split(env, sep)
  39. for i, p := range parts {
  40. parts[i] = strings.TrimSpace(p)
  41. }
  42. *s = parts
  43. return
  44. }
  45. *s = []string{}
  46. }
  47. func StringSlice(s *[]string, name string) {
  48. StringSliceSep(s, name, ",")
  49. }
  50. func StringSliceFile(s *[]string, filepath string) error {
  51. if len(filepath) == 0 {
  52. return nil
  53. }
  54. f, err := os.Open(filepath)
  55. if err != nil {
  56. return fmt.Errorf("Can't open file %s\n", filepath)
  57. }
  58. defer f.Close()
  59. scanner := bufio.NewScanner(f)
  60. for scanner.Scan() {
  61. if str := scanner.Text(); len(str) != 0 && !strings.HasPrefix(str, "#") {
  62. *s = append(*s, str)
  63. }
  64. }
  65. if err := scanner.Err(); err != nil {
  66. return fmt.Errorf("Failed to read presets file: %s", err)
  67. }
  68. return nil
  69. }
  70. func StringMap(m *map[string]string, name string) error {
  71. if env := os.Getenv(name); len(env) > 0 {
  72. mm := make(map[string]string)
  73. keyvalues := strings.Split(env, ";")
  74. for _, keyvalue := range keyvalues {
  75. parts := strings.SplitN(keyvalue, "=", 2)
  76. if len(parts) != 2 {
  77. return fmt.Errorf("Invalid key/value: %s", keyvalue)
  78. }
  79. mm[parts[0]] = parts[1]
  80. }
  81. *m = mm
  82. }
  83. return nil
  84. }
  85. func Bool(b *bool, name string) {
  86. if env, err := strconv.ParseBool(os.Getenv(name)); err == nil {
  87. *b = env
  88. }
  89. }
  90. func URLPath(s *string, name string) {
  91. if env := os.Getenv(name); len(env) > 0 {
  92. if i := strings.IndexByte(env, '?'); i >= 0 {
  93. env = env[:i]
  94. }
  95. if i := strings.IndexByte(env, '#'); i >= 0 {
  96. env = env[:i]
  97. }
  98. if len(env) > 0 && env[len(env)-1] == '/' {
  99. env = env[:len(env)-1]
  100. }
  101. if len(env) > 0 && env[0] != '/' {
  102. env = "/" + env
  103. }
  104. *s = env
  105. }
  106. }
  107. func ImageTypes(it *[]imagetype.Type, name string) error {
  108. if env := os.Getenv(name); len(env) > 0 {
  109. parts := strings.Split(env, ",")
  110. *it = make([]imagetype.Type, 0, len(parts))
  111. for _, p := range parts {
  112. pt := strings.TrimSpace(p)
  113. if t, ok := imagetype.Types[pt]; ok {
  114. *it = append(*it, t)
  115. } else {
  116. return fmt.Errorf("Unknown image format: %s", pt)
  117. }
  118. }
  119. }
  120. return nil
  121. }
  122. func ImageTypesQuality(m map[imagetype.Type]int, name string) error {
  123. if env := os.Getenv(name); len(env) > 0 {
  124. parts := strings.Split(env, ",")
  125. for _, p := range parts {
  126. i := strings.Index(p, "=")
  127. if i < 0 {
  128. return fmt.Errorf("Invalid format quality string: %s", p)
  129. }
  130. imgtypeStr, qStr := strings.TrimSpace(p[:i]), strings.TrimSpace(p[i+1:])
  131. imgtype, ok := imagetype.Types[imgtypeStr]
  132. if !ok {
  133. return fmt.Errorf("Invalid format: %s", p)
  134. }
  135. q, err := strconv.Atoi(qStr)
  136. if err != nil || q <= 0 || q > 100 {
  137. return fmt.Errorf("Invalid quality: %s", p)
  138. }
  139. m[imgtype] = q
  140. }
  141. }
  142. return nil
  143. }
  144. func HexSlice(b *[][]byte, name string) error {
  145. var err error
  146. if env := os.Getenv(name); len(env) > 0 {
  147. parts := strings.Split(env, ",")
  148. keys := make([][]byte, len(parts))
  149. for i, part := range parts {
  150. if keys[i], err = hex.DecodeString(part); err != nil {
  151. return fmt.Errorf("%s expected to be hex-encoded strings. Invalid: %s\n", name, part)
  152. }
  153. }
  154. *b = keys
  155. }
  156. return nil
  157. }
  158. func HexSliceFile(b *[][]byte, filepath string) error {
  159. if len(filepath) == 0 {
  160. return nil
  161. }
  162. f, err := os.Open(filepath)
  163. if err != nil {
  164. return fmt.Errorf("Can't open file %s\n", filepath)
  165. }
  166. keys := [][]byte{}
  167. scanner := bufio.NewScanner(f)
  168. for scanner.Scan() {
  169. part := scanner.Text()
  170. if len(part) == 0 {
  171. continue
  172. }
  173. if key, err := hex.DecodeString(part); err == nil {
  174. keys = append(keys, key)
  175. } else {
  176. return fmt.Errorf("%s expected to contain hex-encoded strings. Invalid: %s\n", filepath, part)
  177. }
  178. }
  179. if err := scanner.Err(); err != nil {
  180. return fmt.Errorf("Failed to read file %s: %s", filepath, err)
  181. }
  182. *b = keys
  183. return nil
  184. }
  185. func Patterns(s *[]*regexp.Regexp, name string) {
  186. if env := os.Getenv(name); len(env) > 0 {
  187. parts := strings.Split(env, ",")
  188. result := make([]*regexp.Regexp, len(parts))
  189. for i, p := range parts {
  190. result[i] = RegexpFromPattern(strings.TrimSpace(p))
  191. }
  192. *s = result
  193. } else {
  194. *s = []*regexp.Regexp{}
  195. }
  196. }
  197. func Replacements(s *[]URLReplacement, name string) error {
  198. if env := os.Getenv(name); len(env) > 0 {
  199. ss := []URLReplacement(nil)
  200. keyvalues := strings.Split(env, ";")
  201. for _, keyvalue := range keyvalues {
  202. parts := strings.SplitN(keyvalue, "=", 2)
  203. if len(parts) != 2 {
  204. return fmt.Errorf("Invalid key/value: %s", keyvalue)
  205. }
  206. ss = append(ss, URLReplacement{
  207. Regexp: RegexpFromPattern(parts[0]),
  208. Replacement: parts[1],
  209. })
  210. }
  211. *s = ss
  212. }
  213. return nil
  214. }
  215. func RegexpFromPattern(pattern string) *regexp.Regexp {
  216. var result strings.Builder
  217. // Perform prefix matching
  218. result.WriteString("^")
  219. for i, part := range strings.Split(pattern, "*") {
  220. // Add a regexp match all without slashes for each wildcard character
  221. if i > 0 {
  222. result.WriteString("([^/]*)")
  223. }
  224. // Quote other parts of the pattern
  225. result.WriteString(regexp.QuoteMeta(part))
  226. }
  227. // It is safe to use regexp.MustCompile since the expression is always valid
  228. return regexp.MustCompile(result.String())
  229. }