durable_streams-rails 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79f28df832bb82db8fe2c8bf0204cf5c6ffededd6755c153d2a00aba4ec7f611
|
|
4
|
+
data.tar.gz: 698ea2440a5422124be37cb82ee2f988ad2e34dd008c702943f30d60797b0510
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 407c2bcb96115a2b19207a3a10d99992b09daa273a39c935ce87adde911df3bc38c7be8b5e5ad409d6313c366ad74607ad9abe3a32eee62a6acfb4b95ce5afd3
|
|
7
|
+
data.tar.gz: 4c91ddfb552668d981f16c355876daa08c616ed42988f4eea157f1848af5b386eb9a93ace96def84a25c7abc9582e3e7d04b24e053b5146e2d15498ec5b95d5a
|
|
@@ -4,6 +4,10 @@ require "erb"
|
|
|
4
4
|
module DurableStreams
|
|
5
5
|
module Rails
|
|
6
6
|
class ServerConfig
|
|
7
|
+
LOG_FORMATS = %w[ console json ].freeze
|
|
8
|
+
LOG_LEVELS = %w[ INFO WARN ERROR DEBUG ].freeze
|
|
9
|
+
STORAGE_DIRECTIVES = %w[ data_dir max_file_handles long_poll_timeout sse_reconnect_interval ].freeze
|
|
10
|
+
|
|
7
11
|
TEMPLATE = ERB.new(<<~'CADDYFILE', trim_mode: "-")
|
|
8
12
|
{
|
|
9
13
|
admin off
|
|
@@ -22,16 +26,21 @@ module DurableStreams
|
|
|
22
26
|
level <%= log_level %>
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
<%= route_block %>
|
|
30
|
+
}
|
|
31
|
+
<%- if internal -%>
|
|
32
|
+
|
|
33
|
+
:<%= internal["port"] %> {
|
|
34
|
+
<%- if internal["bind"] -%>
|
|
35
|
+
bind <%= internal["bind"] %>
|
|
30
36
|
<%- end -%>
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
@allowed remote_ip <%= internal_allowed_ips.join(" ") %>
|
|
38
|
+
handle @allowed {
|
|
39
|
+
<%= route_block(indent: 2) %>
|
|
33
40
|
}
|
|
41
|
+
respond 403
|
|
34
42
|
}
|
|
43
|
+
<%- end -%>
|
|
35
44
|
CADDYFILE
|
|
36
45
|
|
|
37
46
|
def self.generate(config_path, environment)
|
|
@@ -40,6 +49,7 @@ module DurableStreams
|
|
|
40
49
|
|
|
41
50
|
def initialize(config_path, environment)
|
|
42
51
|
@config = YAML.load_file(config_path, aliases: true).fetch(environment)
|
|
52
|
+
validate!
|
|
43
53
|
end
|
|
44
54
|
|
|
45
55
|
def generate
|
|
@@ -47,18 +57,77 @@ module DurableStreams
|
|
|
47
57
|
end
|
|
48
58
|
|
|
49
59
|
private
|
|
50
|
-
def
|
|
51
|
-
|
|
60
|
+
def validate!
|
|
61
|
+
validate_internal!
|
|
62
|
+
validate_log!
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def validate_internal!
|
|
66
|
+
if internal && !internal_allowed_ips
|
|
67
|
+
raise ArgumentError, "internal.allowed_ips is required when internal is configured"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def validate_log!
|
|
72
|
+
if log_config.key?("format") && !LOG_FORMATS.include?(log_config["format"])
|
|
73
|
+
raise ArgumentError,
|
|
74
|
+
"log.format must be one of #{LOG_FORMATS.join(", ")}, got: #{log_config["format"].inspect}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if log_config.key?("level") && !LOG_LEVELS.include?(log_config["level"])
|
|
78
|
+
raise ArgumentError,
|
|
79
|
+
"log.level must be one of #{LOG_LEVELS.join(", ")}, got: #{log_config["level"].inspect}"
|
|
80
|
+
end
|
|
52
81
|
end
|
|
53
82
|
|
|
83
|
+
# Template methods — ordered by invocation
|
|
84
|
+
|
|
54
85
|
def auto_https?
|
|
55
86
|
@config["domain"] && !tls_config
|
|
56
87
|
end
|
|
57
88
|
|
|
89
|
+
def address
|
|
90
|
+
@config["domain"] || ":#{@config.fetch("port", 4437)}"
|
|
91
|
+
end
|
|
92
|
+
|
|
58
93
|
def tls_config
|
|
59
94
|
@config["tls"]
|
|
60
95
|
end
|
|
61
96
|
|
|
97
|
+
def log_format
|
|
98
|
+
log_config.fetch("format", "console")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def log_level
|
|
102
|
+
log_config.fetch("level", "INFO")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def log_config
|
|
106
|
+
@config.fetch("log", {})
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def route_block(indent: 1)
|
|
110
|
+
t = "\t" * indent
|
|
111
|
+
lines = []
|
|
112
|
+
lines << "#{t}route #{route_path} {"
|
|
113
|
+
lines << "#{t}\tforward_auth #{auth["url"]} {"
|
|
114
|
+
lines << "#{t}\t\turi #{auth["path"]}"
|
|
115
|
+
copy_headers.each { |h| lines << "#{t}\t\tcopy_headers #{h}" }
|
|
116
|
+
lines << "#{t}\t}"
|
|
117
|
+
|
|
118
|
+
directives = storage_directives
|
|
119
|
+
if directives.any?
|
|
120
|
+
lines << "#{t}\tdurable_streams {"
|
|
121
|
+
directives.each { |d| lines << "#{t}\t\t#{d}" }
|
|
122
|
+
lines << "#{t}\t}"
|
|
123
|
+
else
|
|
124
|
+
lines << "#{t}\tdurable_streams"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
lines << "#{t}}"
|
|
128
|
+
lines.join("\n")
|
|
129
|
+
end
|
|
130
|
+
|
|
62
131
|
def route_path
|
|
63
132
|
@config.fetch("route", "/v1/streams/*")
|
|
64
133
|
end
|
|
@@ -71,25 +140,17 @@ module DurableStreams
|
|
|
71
140
|
auth.fetch("copy_headers", [ "Cookie" ])
|
|
72
141
|
end
|
|
73
142
|
|
|
74
|
-
def
|
|
75
|
-
@config.fetch("
|
|
143
|
+
def storage_directives
|
|
144
|
+
storage = @config.fetch("storage", {})
|
|
145
|
+
STORAGE_DIRECTIVES.filter_map { |key| "#{key} #{storage[key]}" if storage[key] }
|
|
76
146
|
end
|
|
77
147
|
|
|
78
|
-
def
|
|
79
|
-
@config
|
|
148
|
+
def internal
|
|
149
|
+
@config["internal"]
|
|
80
150
|
end
|
|
81
151
|
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
directives = []
|
|
85
|
-
directives << "data_dir #{storage["data_dir"]}" if storage["data_dir"]
|
|
86
|
-
directives << "max_file_handles #{storage["max_file_handles"]}" if storage["max_file_handles"]
|
|
87
|
-
directives << "long_poll_timeout #{storage["long_poll_timeout"]}" if storage["long_poll_timeout"]
|
|
88
|
-
directives << "sse_reconnect_interval #{storage["sse_reconnect_interval"]}" if storage["sse_reconnect_interval"]
|
|
89
|
-
|
|
90
|
-
if directives.any?
|
|
91
|
-
" {\n" + directives.map { |d| "\t\t\t#{d}" }.join("\n") + "\n\t\t}"
|
|
92
|
-
end
|
|
152
|
+
def internal_allowed_ips
|
|
153
|
+
internal&.dig("allowed_ips")
|
|
93
154
|
end
|
|
94
155
|
end
|
|
95
156
|
end
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
default: &default
|
|
8
8
|
port: 4437
|
|
9
|
-
# route: /v1/streams/*
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# route: /v1/streams/* # URL path prefix for stream endpoints. Default: /v1/streams/*
|
|
10
|
+
# log:
|
|
11
|
+
# format: console # "console" (human-readable) or "json" (structured). Default: console
|
|
12
|
+
# level: INFO # INFO, WARN, or ERROR. Default: INFO
|
|
12
13
|
auth:
|
|
13
14
|
url: http://localhost:3000
|
|
14
15
|
path: /durable_streams/auth/verify
|
|
@@ -24,11 +25,17 @@ test:
|
|
|
24
25
|
production:
|
|
25
26
|
<<: *default
|
|
26
27
|
domain: streams.example.com
|
|
27
|
-
# tls:
|
|
28
|
+
# tls: # Explicit TLS certs (disables ACME). Use with Cloudflare origin certs.
|
|
28
29
|
# cert: /etc/caddy/certs/cert.pem
|
|
29
30
|
# key: /etc/caddy/certs/key.pem
|
|
30
|
-
#
|
|
31
|
-
#
|
|
31
|
+
# internal: # Server-to-server listener (no TLS). Restrict to private network.
|
|
32
|
+
# port: 4437
|
|
33
|
+
# bind: 10.0.0.5 # Only listen on the private interface.
|
|
34
|
+
# allowed_ips: # Caddy remote_ip check — 403 for everyone else.
|
|
35
|
+
# - 10.0.0.4
|
|
36
|
+
# log:
|
|
37
|
+
# format: json
|
|
38
|
+
# level: WARN
|
|
32
39
|
auth:
|
|
33
40
|
url: https://app.example.com
|
|
34
41
|
path: /durable_streams/auth/verify
|
|
@@ -37,6 +44,6 @@ production:
|
|
|
37
44
|
- Authorization
|
|
38
45
|
storage:
|
|
39
46
|
data_dir: /var/data/durable-streams
|
|
40
|
-
# max_file_handles: 100
|
|
41
|
-
# long_poll_timeout: 30s
|
|
42
|
-
# sse_reconnect_interval: 60s
|
|
47
|
+
# max_file_handles: 100 # Connection pool for segment files. Default: 100
|
|
48
|
+
# long_poll_timeout: 30s # How long to wait for new data before returning 204. Default: 30s
|
|
49
|
+
# sse_reconnect_interval: 60s # Server closes SSE after this interval for CDN collapsing. Default: 60s
|