spikard 0.3.1 → 0.3.3
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 +4 -4
- data/LICENSE +1 -1
- data/README.md +659 -659
- data/ext/spikard_rb/Cargo.toml +17 -17
- data/ext/spikard_rb/extconf.rb +10 -10
- data/ext/spikard_rb/src/lib.rs +6 -6
- data/lib/spikard/app.rb +386 -386
- data/lib/spikard/background.rb +27 -27
- data/lib/spikard/config.rb +396 -396
- data/lib/spikard/converters.rb +13 -13
- data/lib/spikard/handler_wrapper.rb +113 -113
- data/lib/spikard/provide.rb +214 -214
- data/lib/spikard/response.rb +173 -173
- data/lib/spikard/schema.rb +243 -243
- data/lib/spikard/sse.rb +111 -111
- data/lib/spikard/streaming_response.rb +44 -44
- data/lib/spikard/testing.rb +221 -221
- data/lib/spikard/upload_file.rb +131 -131
- data/lib/spikard/version.rb +5 -5
- data/lib/spikard/websocket.rb +59 -59
- data/lib/spikard.rb +43 -43
- data/sig/spikard.rbs +360 -360
- data/vendor/crates/spikard-core/Cargo.toml +40 -0
- data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -0
- data/vendor/crates/spikard-core/src/bindings/response.rs +133 -0
- data/vendor/crates/spikard-core/src/debug.rs +63 -0
- data/vendor/crates/spikard-core/src/di/container.rs +726 -0
- data/vendor/crates/spikard-core/src/di/dependency.rs +273 -0
- data/vendor/crates/spikard-core/src/di/error.rs +118 -0
- data/vendor/crates/spikard-core/src/di/factory.rs +538 -0
- data/vendor/crates/spikard-core/src/di/graph.rs +545 -0
- data/vendor/crates/spikard-core/src/di/mod.rs +192 -0
- data/vendor/crates/spikard-core/src/di/resolved.rs +411 -0
- data/vendor/crates/spikard-core/src/di/value.rs +283 -0
- data/vendor/crates/spikard-core/src/errors.rs +39 -0
- data/vendor/crates/spikard-core/src/http.rs +153 -0
- data/vendor/crates/spikard-core/src/lib.rs +29 -0
- data/vendor/crates/spikard-core/src/lifecycle.rs +422 -0
- data/vendor/crates/spikard-core/src/parameters.rs +722 -0
- data/vendor/crates/spikard-core/src/problem.rs +310 -0
- data/vendor/crates/spikard-core/src/request_data.rs +189 -0
- data/vendor/crates/spikard-core/src/router.rs +249 -0
- data/vendor/crates/spikard-core/src/schema_registry.rs +183 -0
- data/vendor/crates/spikard-core/src/type_hints.rs +304 -0
- data/vendor/crates/spikard-core/src/validation.rs +699 -0
- data/vendor/crates/spikard-http/Cargo.toml +58 -0
- data/vendor/crates/spikard-http/src/auth.rs +247 -0
- data/vendor/crates/spikard-http/src/background.rs +249 -0
- data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -0
- data/vendor/crates/spikard-http/src/bindings/response.rs +1 -0
- data/vendor/crates/spikard-http/src/body_metadata.rs +8 -0
- data/vendor/crates/spikard-http/src/cors.rs +490 -0
- data/vendor/crates/spikard-http/src/debug.rs +63 -0
- data/vendor/crates/spikard-http/src/di_handler.rs +423 -0
- data/vendor/crates/spikard-http/src/handler_response.rs +190 -0
- data/vendor/crates/spikard-http/src/handler_trait.rs +228 -0
- data/vendor/crates/spikard-http/src/handler_trait_tests.rs +284 -0
- data/vendor/crates/spikard-http/src/lib.rs +529 -0
- data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +149 -0
- data/vendor/crates/spikard-http/src/lifecycle.rs +428 -0
- data/vendor/crates/spikard-http/src/middleware/mod.rs +285 -0
- data/vendor/crates/spikard-http/src/middleware/multipart.rs +86 -0
- data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +147 -0
- data/vendor/crates/spikard-http/src/middleware/validation.rs +287 -0
- data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -0
- data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +190 -0
- data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +308 -0
- data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +195 -0
- data/vendor/crates/spikard-http/src/parameters.rs +1 -0
- data/vendor/crates/spikard-http/src/problem.rs +1 -0
- data/vendor/crates/spikard-http/src/query_parser.rs +369 -0
- data/vendor/crates/spikard-http/src/response.rs +399 -0
- data/vendor/crates/spikard-http/src/router.rs +1 -0
- data/vendor/crates/spikard-http/src/schema_registry.rs +1 -0
- data/vendor/crates/spikard-http/src/server/handler.rs +87 -0
- data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +98 -0
- data/vendor/crates/spikard-http/src/server/mod.rs +805 -0
- data/vendor/crates/spikard-http/src/server/request_extraction.rs +119 -0
- data/vendor/crates/spikard-http/src/sse.rs +447 -0
- data/vendor/crates/spikard-http/src/testing/form.rs +14 -0
- data/vendor/crates/spikard-http/src/testing/multipart.rs +60 -0
- data/vendor/crates/spikard-http/src/testing/test_client.rs +285 -0
- data/vendor/crates/spikard-http/src/testing.rs +377 -0
- data/vendor/crates/spikard-http/src/type_hints.rs +1 -0
- data/vendor/crates/spikard-http/src/validation.rs +1 -0
- data/vendor/crates/spikard-http/src/websocket.rs +324 -0
- data/vendor/crates/spikard-rb/Cargo.toml +42 -0
- data/vendor/crates/spikard-rb/build.rs +8 -0
- data/vendor/crates/spikard-rb/src/background.rs +63 -0
- data/vendor/crates/spikard-rb/src/config.rs +294 -0
- data/vendor/crates/spikard-rb/src/conversion.rs +453 -0
- data/vendor/crates/spikard-rb/src/di.rs +409 -0
- data/vendor/crates/spikard-rb/src/handler.rs +625 -0
- data/vendor/crates/spikard-rb/src/lib.rs +2771 -0
- data/vendor/crates/spikard-rb/src/lifecycle.rs +274 -0
- data/vendor/crates/spikard-rb/src/server.rs +283 -0
- data/vendor/crates/spikard-rb/src/sse.rs +231 -0
- data/vendor/crates/spikard-rb/src/test_client.rs +404 -0
- data/vendor/crates/spikard-rb/src/test_sse.rs +143 -0
- data/vendor/crates/spikard-rb/src/test_websocket.rs +221 -0
- data/vendor/crates/spikard-rb/src/websocket.rs +233 -0
- data/vendor/spikard-core/Cargo.toml +40 -0
- data/vendor/spikard-core/src/bindings/mod.rs +3 -0
- data/vendor/spikard-core/src/bindings/response.rs +133 -0
- data/vendor/spikard-core/src/debug.rs +63 -0
- data/vendor/spikard-core/src/di/container.rs +726 -0
- data/vendor/spikard-core/src/di/dependency.rs +273 -0
- data/vendor/spikard-core/src/di/error.rs +118 -0
- data/vendor/spikard-core/src/di/factory.rs +538 -0
- data/vendor/spikard-core/src/di/graph.rs +545 -0
- data/vendor/spikard-core/src/di/mod.rs +192 -0
- data/vendor/spikard-core/src/di/resolved.rs +411 -0
- data/vendor/spikard-core/src/di/value.rs +283 -0
- data/vendor/spikard-core/src/http.rs +153 -0
- data/vendor/spikard-core/src/lib.rs +28 -0
- data/vendor/spikard-core/src/lifecycle.rs +422 -0
- data/vendor/spikard-core/src/parameters.rs +719 -0
- data/vendor/spikard-core/src/problem.rs +310 -0
- data/vendor/spikard-core/src/request_data.rs +189 -0
- data/vendor/spikard-core/src/router.rs +249 -0
- data/vendor/spikard-core/src/schema_registry.rs +183 -0
- data/vendor/spikard-core/src/type_hints.rs +304 -0
- data/vendor/spikard-core/src/validation.rs +699 -0
- data/vendor/spikard-http/Cargo.toml +58 -0
- data/vendor/spikard-http/src/auth.rs +247 -0
- data/vendor/spikard-http/src/background.rs +249 -0
- data/vendor/spikard-http/src/bindings/mod.rs +3 -0
- data/vendor/spikard-http/src/bindings/response.rs +1 -0
- data/vendor/spikard-http/src/body_metadata.rs +8 -0
- data/vendor/spikard-http/src/cors.rs +490 -0
- data/vendor/spikard-http/src/debug.rs +63 -0
- data/vendor/spikard-http/src/di_handler.rs +423 -0
- data/vendor/spikard-http/src/handler_response.rs +190 -0
- data/vendor/spikard-http/src/handler_trait.rs +228 -0
- data/vendor/spikard-http/src/handler_trait_tests.rs +284 -0
- data/vendor/spikard-http/src/lib.rs +529 -0
- data/vendor/spikard-http/src/lifecycle/adapter.rs +149 -0
- data/vendor/spikard-http/src/lifecycle.rs +428 -0
- data/vendor/spikard-http/src/middleware/mod.rs +285 -0
- data/vendor/spikard-http/src/middleware/multipart.rs +86 -0
- data/vendor/spikard-http/src/middleware/urlencoded.rs +147 -0
- data/vendor/spikard-http/src/middleware/validation.rs +287 -0
- data/vendor/spikard-http/src/openapi/mod.rs +309 -0
- data/vendor/spikard-http/src/openapi/parameter_extraction.rs +190 -0
- data/vendor/spikard-http/src/openapi/schema_conversion.rs +308 -0
- data/vendor/spikard-http/src/openapi/spec_generation.rs +195 -0
- data/vendor/spikard-http/src/parameters.rs +1 -0
- data/vendor/spikard-http/src/problem.rs +1 -0
- data/vendor/spikard-http/src/query_parser.rs +369 -0
- data/vendor/spikard-http/src/response.rs +399 -0
- data/vendor/spikard-http/src/router.rs +1 -0
- data/vendor/spikard-http/src/schema_registry.rs +1 -0
- data/vendor/spikard-http/src/server/handler.rs +80 -0
- data/vendor/spikard-http/src/server/lifecycle_execution.rs +98 -0
- data/vendor/spikard-http/src/server/mod.rs +805 -0
- data/vendor/spikard-http/src/server/request_extraction.rs +119 -0
- data/vendor/spikard-http/src/sse.rs +447 -0
- data/vendor/spikard-http/src/testing/form.rs +14 -0
- data/vendor/spikard-http/src/testing/multipart.rs +60 -0
- data/vendor/spikard-http/src/testing/test_client.rs +285 -0
- data/vendor/spikard-http/src/testing.rs +377 -0
- data/vendor/spikard-http/src/type_hints.rs +1 -0
- data/vendor/spikard-http/src/validation.rs +1 -0
- data/vendor/spikard-http/src/websocket.rs +324 -0
- data/vendor/spikard-rb/Cargo.toml +42 -0
- data/vendor/spikard-rb/build.rs +8 -0
- data/vendor/spikard-rb/src/background.rs +63 -0
- data/vendor/spikard-rb/src/config.rs +294 -0
- data/vendor/spikard-rb/src/conversion.rs +392 -0
- data/vendor/spikard-rb/src/di.rs +409 -0
- data/vendor/spikard-rb/src/handler.rs +534 -0
- data/vendor/spikard-rb/src/lib.rs +2020 -0
- data/vendor/spikard-rb/src/lifecycle.rs +267 -0
- data/vendor/spikard-rb/src/server.rs +283 -0
- data/vendor/spikard-rb/src/sse.rs +231 -0
- data/vendor/spikard-rb/src/test_client.rs +404 -0
- data/vendor/spikard-rb/src/test_sse.rs +143 -0
- data/vendor/spikard-rb/src/test_websocket.rs +221 -0
- data/vendor/spikard-rb/src/websocket.rs +233 -0
- metadata +158 -1
data/lib/spikard/config.rb
CHANGED
|
@@ -1,396 +1,396 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Spikard
|
|
4
|
-
# Compression configuration for response compression middleware.
|
|
5
|
-
#
|
|
6
|
-
# Spikard supports gzip and brotli compression for responses.
|
|
7
|
-
# Compression is applied based on Accept-Encoding headers.
|
|
8
|
-
#
|
|
9
|
-
# @example
|
|
10
|
-
# compression = CompressionConfig.new(
|
|
11
|
-
# gzip: true,
|
|
12
|
-
# brotli: true,
|
|
13
|
-
# min_size: 1024,
|
|
14
|
-
# quality: 6
|
|
15
|
-
# )
|
|
16
|
-
class CompressionConfig
|
|
17
|
-
attr_accessor :gzip, :brotli, :min_size, :quality
|
|
18
|
-
|
|
19
|
-
# @param gzip [Boolean] Enable gzip compression (default: true)
|
|
20
|
-
# @param brotli [Boolean] Enable brotli compression (default: true)
|
|
21
|
-
# @param min_size [Integer] Minimum response size in bytes to compress (default: 1024)
|
|
22
|
-
# @param quality [Integer] Compression quality level (0-11 for brotli, 0-9 for gzip, default: 6)
|
|
23
|
-
def initialize(gzip: true, brotli: true, min_size: 1024, quality: 6)
|
|
24
|
-
@gzip = gzip
|
|
25
|
-
@brotli = brotli
|
|
26
|
-
@min_size = min_size
|
|
27
|
-
@quality = quality
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Rate limiting configuration using Generic Cell Rate Algorithm (GCRA).
|
|
32
|
-
#
|
|
33
|
-
# By default, rate limits are applied per IP address.
|
|
34
|
-
#
|
|
35
|
-
# @example
|
|
36
|
-
# rate_limit = RateLimitConfig.new(
|
|
37
|
-
# per_second: 100,
|
|
38
|
-
# burst: 200,
|
|
39
|
-
# ip_based: true
|
|
40
|
-
# )
|
|
41
|
-
class RateLimitConfig
|
|
42
|
-
attr_accessor :per_second, :burst, :ip_based
|
|
43
|
-
|
|
44
|
-
# @param per_second [Integer] Maximum requests per second
|
|
45
|
-
# @param burst [Integer] Burst allowance - allows temporary spikes
|
|
46
|
-
# @param ip_based [Boolean] Apply rate limits per IP address (default: true)
|
|
47
|
-
def initialize(per_second:, burst:, ip_based: true)
|
|
48
|
-
@per_second = per_second
|
|
49
|
-
@burst = burst
|
|
50
|
-
@ip_based = ip_based
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# JWT authentication configuration.
|
|
55
|
-
#
|
|
56
|
-
# Validates JWT tokens using the specified secret and algorithm.
|
|
57
|
-
# Tokens are expected in the Authorization header as "Bearer <token>".
|
|
58
|
-
#
|
|
59
|
-
# Supported algorithms:
|
|
60
|
-
# - HS256, HS384, HS512 (HMAC with SHA)
|
|
61
|
-
# - RS256, RS384, RS512 (RSA signatures)
|
|
62
|
-
# - ES256, ES384, ES512 (ECDSA signatures)
|
|
63
|
-
# - PS256, PS384, PS512 (RSA-PSS signatures)
|
|
64
|
-
#
|
|
65
|
-
# @example
|
|
66
|
-
# jwt = JwtConfig.new(
|
|
67
|
-
# secret: 'your-secret-key',
|
|
68
|
-
# algorithm: 'HS256',
|
|
69
|
-
# audience: ['api.example.com'],
|
|
70
|
-
# issuer: 'auth.example.com',
|
|
71
|
-
# leeway: 30
|
|
72
|
-
# )
|
|
73
|
-
class JwtConfig
|
|
74
|
-
attr_accessor :secret, :algorithm, :audience, :issuer, :leeway
|
|
75
|
-
|
|
76
|
-
# @param secret [String] Secret key for JWT validation
|
|
77
|
-
# @param algorithm [String] JWT algorithm (default: "HS256")
|
|
78
|
-
# @param audience [Array<String>, nil] Expected audience claim(s)
|
|
79
|
-
# @param issuer [String, nil] Expected issuer claim
|
|
80
|
-
# @param leeway [Integer] Time leeway in seconds for exp/nbf/iat claims (default: 0)
|
|
81
|
-
def initialize(secret:, algorithm: 'HS256', audience: nil, issuer: nil, leeway: 0)
|
|
82
|
-
@secret = secret
|
|
83
|
-
@algorithm = algorithm
|
|
84
|
-
@audience = audience
|
|
85
|
-
@issuer = issuer
|
|
86
|
-
@leeway = leeway
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# API key authentication configuration.
|
|
91
|
-
#
|
|
92
|
-
# Validates API keys from request headers. Keys are matched exactly.
|
|
93
|
-
#
|
|
94
|
-
# @example
|
|
95
|
-
# api_key = ApiKeyConfig.new(
|
|
96
|
-
# keys: ['key-1', 'key-2', 'key-3'],
|
|
97
|
-
# header_name: 'X-API-Key'
|
|
98
|
-
# )
|
|
99
|
-
class ApiKeyConfig
|
|
100
|
-
attr_accessor :keys, :header_name
|
|
101
|
-
|
|
102
|
-
# @param keys [Array<String>] List of valid API keys
|
|
103
|
-
# @param header_name [String] HTTP header name to check for API key (default: "X-API-Key")
|
|
104
|
-
def initialize(keys:, header_name: 'X-API-Key')
|
|
105
|
-
@keys = keys
|
|
106
|
-
@header_name = header_name
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Static file serving configuration.
|
|
111
|
-
#
|
|
112
|
-
# Serves files from a directory at a given route prefix.
|
|
113
|
-
# Multiple static file configurations can be registered.
|
|
114
|
-
#
|
|
115
|
-
# @example
|
|
116
|
-
# static = StaticFilesConfig.new(
|
|
117
|
-
# directory: './public',
|
|
118
|
-
# route_prefix: '/static',
|
|
119
|
-
# index_file: true,
|
|
120
|
-
# cache_control: 'public, max-age=3600'
|
|
121
|
-
# )
|
|
122
|
-
class StaticFilesConfig
|
|
123
|
-
attr_accessor :directory, :route_prefix, :index_file, :cache_control
|
|
124
|
-
|
|
125
|
-
# @param directory [String] Directory path containing static files
|
|
126
|
-
# @param route_prefix [String] URL prefix for serving static files (e.g., "/static")
|
|
127
|
-
# @param index_file [Boolean] Serve index.html for directory requests (default: true)
|
|
128
|
-
# @param cache_control [String, nil] Optional Cache-Control header value (e.g., "public, max-age=3600")
|
|
129
|
-
def initialize(directory:, route_prefix:, index_file: true, cache_control: nil)
|
|
130
|
-
@directory = directory
|
|
131
|
-
@route_prefix = route_prefix
|
|
132
|
-
@index_file = index_file
|
|
133
|
-
@cache_control = cache_control
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Contact information for OpenAPI documentation.
|
|
138
|
-
#
|
|
139
|
-
# @example
|
|
140
|
-
# contact = ContactInfo.new(
|
|
141
|
-
# name: 'API Team',
|
|
142
|
-
# email: 'api@example.com',
|
|
143
|
-
# url: 'https://example.com'
|
|
144
|
-
# )
|
|
145
|
-
class ContactInfo
|
|
146
|
-
attr_accessor :name, :email, :url
|
|
147
|
-
|
|
148
|
-
# @param name [String, nil] Name of the contact person/organization
|
|
149
|
-
# @param email [String, nil] Email address for contact
|
|
150
|
-
# @param url [String, nil] URL for contact information
|
|
151
|
-
def initialize(name: nil, email: nil, url: nil)
|
|
152
|
-
@name = name
|
|
153
|
-
@email = email
|
|
154
|
-
@url = url
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# License information for OpenAPI documentation.
|
|
159
|
-
#
|
|
160
|
-
# @example
|
|
161
|
-
# license = LicenseInfo.new(
|
|
162
|
-
# name: 'MIT',
|
|
163
|
-
# url: 'https://opensource.org/licenses/MIT'
|
|
164
|
-
# )
|
|
165
|
-
class LicenseInfo
|
|
166
|
-
attr_accessor :name, :url
|
|
167
|
-
|
|
168
|
-
# @param name [String] License name (e.g., "MIT", "Apache 2.0")
|
|
169
|
-
# @param url [String, nil] URL to the full license text
|
|
170
|
-
def initialize(name:, url: nil)
|
|
171
|
-
@name = name
|
|
172
|
-
@url = url
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Server information for OpenAPI documentation.
|
|
177
|
-
#
|
|
178
|
-
# Multiple servers can be specified for different environments.
|
|
179
|
-
#
|
|
180
|
-
# @example
|
|
181
|
-
# server = ServerInfo.new(
|
|
182
|
-
# url: 'https://api.example.com',
|
|
183
|
-
# description: 'Production'
|
|
184
|
-
# )
|
|
185
|
-
class ServerInfo
|
|
186
|
-
attr_accessor :url, :description
|
|
187
|
-
|
|
188
|
-
# @param url [String] Server URL (e.g., "https://api.example.com")
|
|
189
|
-
# @param description [String, nil] Description of the server (e.g., "Production", "Staging")
|
|
190
|
-
def initialize(url:, description: nil)
|
|
191
|
-
@url = url
|
|
192
|
-
@description = description
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
# Security scheme configuration for OpenAPI documentation.
|
|
197
|
-
#
|
|
198
|
-
# Supports HTTP (Bearer/JWT) and API Key authentication schemes.
|
|
199
|
-
#
|
|
200
|
-
# @example HTTP Bearer
|
|
201
|
-
# scheme = SecuritySchemeInfo.new(
|
|
202
|
-
# type: 'http',
|
|
203
|
-
# scheme: 'bearer',
|
|
204
|
-
# bearer_format: 'JWT'
|
|
205
|
-
# )
|
|
206
|
-
#
|
|
207
|
-
# @example API Key
|
|
208
|
-
# scheme = SecuritySchemeInfo.new(
|
|
209
|
-
# type: 'apiKey',
|
|
210
|
-
# location: 'header',
|
|
211
|
-
# name: 'X-API-Key'
|
|
212
|
-
# )
|
|
213
|
-
class SecuritySchemeInfo
|
|
214
|
-
attr_accessor :type, :scheme, :bearer_format, :location, :name
|
|
215
|
-
|
|
216
|
-
# @param type [String] Security scheme type ("http" or "apiKey")
|
|
217
|
-
# @param scheme [String, nil] HTTP scheme (e.g., "bearer", "basic") - for type="http"
|
|
218
|
-
# @param bearer_format [String, nil] Format hint for Bearer tokens (e.g., "JWT") - for type="http"
|
|
219
|
-
# @param location [String, nil] Where to look for the API key ("header", "query", or "cookie") - for type="apiKey"
|
|
220
|
-
# @param name [String, nil] Parameter name (e.g., "X-API-Key") - for type="apiKey"
|
|
221
|
-
def initialize(type:, scheme: nil, bearer_format: nil, location: nil, name: nil)
|
|
222
|
-
@type = type
|
|
223
|
-
@scheme = scheme
|
|
224
|
-
@bearer_format = bearer_format
|
|
225
|
-
@location = location
|
|
226
|
-
@name = name
|
|
227
|
-
|
|
228
|
-
validate!
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
private
|
|
232
|
-
|
|
233
|
-
def validate!
|
|
234
|
-
case @type
|
|
235
|
-
when 'http'
|
|
236
|
-
raise ArgumentError, 'scheme is required for type="http"' if @scheme.nil?
|
|
237
|
-
when 'apiKey'
|
|
238
|
-
raise ArgumentError, 'location and name are required for type="apiKey"' if @location.nil? || @name.nil?
|
|
239
|
-
else
|
|
240
|
-
raise ArgumentError, "type must be 'http' or 'apiKey', got: #{@type.inspect}"
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
# OpenAPI 3.1.0 documentation configuration.
|
|
246
|
-
#
|
|
247
|
-
# Spikard can automatically generate OpenAPI documentation from your routes.
|
|
248
|
-
# When enabled, it serves:
|
|
249
|
-
# - Swagger UI at /docs (customizable)
|
|
250
|
-
# - Redoc at /redoc (customizable)
|
|
251
|
-
# - OpenAPI JSON spec at /openapi.json (customizable)
|
|
252
|
-
#
|
|
253
|
-
# Security schemes are auto-detected from middleware configuration.
|
|
254
|
-
# Schemas are generated from your route type hints and validation.
|
|
255
|
-
#
|
|
256
|
-
# @example
|
|
257
|
-
# openapi = OpenApiConfig.new(
|
|
258
|
-
# enabled: true,
|
|
259
|
-
# title: 'My API',
|
|
260
|
-
# version: '1.0.0',
|
|
261
|
-
# description: 'A great API built with Spikard',
|
|
262
|
-
# contact: ContactInfo.new(
|
|
263
|
-
# name: 'API Team',
|
|
264
|
-
# email: 'api@example.com',
|
|
265
|
-
# url: 'https://example.com'
|
|
266
|
-
# ),
|
|
267
|
-
# license: LicenseInfo.new(
|
|
268
|
-
# name: 'MIT',
|
|
269
|
-
# url: 'https://opensource.org/licenses/MIT'
|
|
270
|
-
# ),
|
|
271
|
-
# servers: [
|
|
272
|
-
# ServerInfo.new(url: 'https://api.example.com', description: 'Production'),
|
|
273
|
-
# ServerInfo.new(url: 'http://localhost:8000', description: 'Development')
|
|
274
|
-
# ]
|
|
275
|
-
# )
|
|
276
|
-
class OpenApiConfig
|
|
277
|
-
attr_accessor :enabled, :title, :version, :description,
|
|
278
|
-
:swagger_ui_path, :redoc_path, :openapi_json_path,
|
|
279
|
-
:contact, :license, :servers, :security_schemes
|
|
280
|
-
|
|
281
|
-
# @param enabled [Boolean] Enable OpenAPI generation (default: false for zero overhead)
|
|
282
|
-
# @param title [String] API title (default: "API")
|
|
283
|
-
# @param version [String] API version (default: "1.0.0")
|
|
284
|
-
# @param description [String, nil] API description (supports Markdown)
|
|
285
|
-
# @param swagger_ui_path [String] Path to serve Swagger UI (default: "/docs")
|
|
286
|
-
# @param redoc_path [String] Path to serve Redoc (default: "/redoc")
|
|
287
|
-
# @param openapi_json_path [String] Path to serve OpenAPI JSON spec (default: "/openapi.json")
|
|
288
|
-
# @param contact [ContactInfo, nil] Contact information for the API
|
|
289
|
-
# @param license [LicenseInfo, nil] License information for the API
|
|
290
|
-
# @param servers [Array<ServerInfo>] List of server URLs for different environments (default: [])
|
|
291
|
-
# @param security_schemes [Hash<String, SecuritySchemeInfo>] Custom security schemes (auto-detected if not provided)
|
|
292
|
-
def initialize(
|
|
293
|
-
enabled: false,
|
|
294
|
-
title: 'API',
|
|
295
|
-
version: '1.0.0',
|
|
296
|
-
description: nil,
|
|
297
|
-
swagger_ui_path: '/docs',
|
|
298
|
-
redoc_path: '/redoc',
|
|
299
|
-
openapi_json_path: '/openapi.json',
|
|
300
|
-
contact: nil,
|
|
301
|
-
license: nil,
|
|
302
|
-
servers: [],
|
|
303
|
-
security_schemes: {}
|
|
304
|
-
)
|
|
305
|
-
@enabled = enabled
|
|
306
|
-
@title = title
|
|
307
|
-
@version = version
|
|
308
|
-
@description = description
|
|
309
|
-
@swagger_ui_path = swagger_ui_path
|
|
310
|
-
@redoc_path = redoc_path
|
|
311
|
-
@openapi_json_path = openapi_json_path
|
|
312
|
-
@contact = contact
|
|
313
|
-
@license = license
|
|
314
|
-
@servers = servers
|
|
315
|
-
@security_schemes = security_schemes
|
|
316
|
-
end
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
# Complete server configuration for Spikard.
|
|
320
|
-
#
|
|
321
|
-
# This is the main configuration object that controls all aspects of the server
|
|
322
|
-
# including network settings, middleware, authentication, and more.
|
|
323
|
-
#
|
|
324
|
-
# @example
|
|
325
|
-
# config = ServerConfig.new(
|
|
326
|
-
# host: '0.0.0.0',
|
|
327
|
-
# port: 8080,
|
|
328
|
-
# workers: 4,
|
|
329
|
-
# compression: CompressionConfig.new(quality: 9),
|
|
330
|
-
# rate_limit: RateLimitConfig.new(per_second: 100, burst: 200),
|
|
331
|
-
# static_files: [
|
|
332
|
-
# StaticFilesConfig.new(
|
|
333
|
-
# directory: './public',
|
|
334
|
-
# route_prefix: '/static'
|
|
335
|
-
# )
|
|
336
|
-
# ],
|
|
337
|
-
# openapi: OpenApiConfig.new(
|
|
338
|
-
# enabled: true,
|
|
339
|
-
# title: 'My API',
|
|
340
|
-
# version: '1.0.0'
|
|
341
|
-
# )
|
|
342
|
-
# )
|
|
343
|
-
class ServerConfig
|
|
344
|
-
attr_accessor :host, :port, :workers,
|
|
345
|
-
:enable_request_id, :max_body_size, :request_timeout,
|
|
346
|
-
:compression, :rate_limit, :jwt_auth, :api_key_auth,
|
|
347
|
-
:static_files, :graceful_shutdown, :shutdown_timeout,
|
|
348
|
-
:openapi
|
|
349
|
-
|
|
350
|
-
# @param host [String] Host address to bind to (default: "127.0.0.1")
|
|
351
|
-
# @param port [Integer] Port number to listen on (default: 8000, range: 1-65535)
|
|
352
|
-
# @param workers [Integer] Number of worker processes (default: 1)
|
|
353
|
-
# @param enable_request_id [Boolean] Add X-Request-ID header to responses (default: true)
|
|
354
|
-
# @param max_body_size [Integer, nil] Maximum request body size in bytes (default: 10MB, nil for unlimited)
|
|
355
|
-
# @param request_timeout [Integer, nil] Request timeout in seconds (default: 30, nil for no timeout)
|
|
356
|
-
# @param compression [CompressionConfig, nil] Response compression configuration (default: enabled with defaults)
|
|
357
|
-
# @param rate_limit [RateLimitConfig, nil] Rate limiting configuration (default: nil/disabled)
|
|
358
|
-
# @param jwt_auth [JwtConfig, nil] JWT authentication configuration (default: nil/disabled)
|
|
359
|
-
# @param api_key_auth [ApiKeyConfig, nil] API key authentication configuration (default: nil/disabled)
|
|
360
|
-
# @param static_files [Array<StaticFilesConfig>] List of static file serving configurations (default: [])
|
|
361
|
-
# @param graceful_shutdown [Boolean] Enable graceful shutdown (default: true)
|
|
362
|
-
# @param shutdown_timeout [Integer] Graceful shutdown timeout in seconds (default: 30)
|
|
363
|
-
# @param openapi [OpenApiConfig, nil] OpenAPI configuration (default: nil/disabled)
|
|
364
|
-
def initialize(
|
|
365
|
-
host: '127.0.0.1',
|
|
366
|
-
port: 8000,
|
|
367
|
-
workers: 1,
|
|
368
|
-
enable_request_id: true,
|
|
369
|
-
max_body_size: 10 * 1024 * 1024, # 10MB
|
|
370
|
-
request_timeout: 30,
|
|
371
|
-
compression: CompressionConfig.new,
|
|
372
|
-
rate_limit: nil,
|
|
373
|
-
jwt_auth: nil,
|
|
374
|
-
api_key_auth: nil,
|
|
375
|
-
static_files: [],
|
|
376
|
-
graceful_shutdown: true,
|
|
377
|
-
shutdown_timeout: 30,
|
|
378
|
-
openapi: nil
|
|
379
|
-
)
|
|
380
|
-
@host = host
|
|
381
|
-
@port = port
|
|
382
|
-
@workers = workers
|
|
383
|
-
@enable_request_id = enable_request_id
|
|
384
|
-
@max_body_size = max_body_size
|
|
385
|
-
@request_timeout = request_timeout
|
|
386
|
-
@compression = compression
|
|
387
|
-
@rate_limit = rate_limit
|
|
388
|
-
@jwt_auth = jwt_auth
|
|
389
|
-
@api_key_auth = api_key_auth
|
|
390
|
-
@static_files = static_files
|
|
391
|
-
@graceful_shutdown = graceful_shutdown
|
|
392
|
-
@shutdown_timeout = shutdown_timeout
|
|
393
|
-
@openapi = openapi
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spikard
|
|
4
|
+
# Compression configuration for response compression middleware.
|
|
5
|
+
#
|
|
6
|
+
# Spikard supports gzip and brotli compression for responses.
|
|
7
|
+
# Compression is applied based on Accept-Encoding headers.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# compression = CompressionConfig.new(
|
|
11
|
+
# gzip: true,
|
|
12
|
+
# brotli: true,
|
|
13
|
+
# min_size: 1024,
|
|
14
|
+
# quality: 6
|
|
15
|
+
# )
|
|
16
|
+
class CompressionConfig
|
|
17
|
+
attr_accessor :gzip, :brotli, :min_size, :quality
|
|
18
|
+
|
|
19
|
+
# @param gzip [Boolean] Enable gzip compression (default: true)
|
|
20
|
+
# @param brotli [Boolean] Enable brotli compression (default: true)
|
|
21
|
+
# @param min_size [Integer] Minimum response size in bytes to compress (default: 1024)
|
|
22
|
+
# @param quality [Integer] Compression quality level (0-11 for brotli, 0-9 for gzip, default: 6)
|
|
23
|
+
def initialize(gzip: true, brotli: true, min_size: 1024, quality: 6)
|
|
24
|
+
@gzip = gzip
|
|
25
|
+
@brotli = brotli
|
|
26
|
+
@min_size = min_size
|
|
27
|
+
@quality = quality
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Rate limiting configuration using Generic Cell Rate Algorithm (GCRA).
|
|
32
|
+
#
|
|
33
|
+
# By default, rate limits are applied per IP address.
|
|
34
|
+
#
|
|
35
|
+
# @example
|
|
36
|
+
# rate_limit = RateLimitConfig.new(
|
|
37
|
+
# per_second: 100,
|
|
38
|
+
# burst: 200,
|
|
39
|
+
# ip_based: true
|
|
40
|
+
# )
|
|
41
|
+
class RateLimitConfig
|
|
42
|
+
attr_accessor :per_second, :burst, :ip_based
|
|
43
|
+
|
|
44
|
+
# @param per_second [Integer] Maximum requests per second
|
|
45
|
+
# @param burst [Integer] Burst allowance - allows temporary spikes
|
|
46
|
+
# @param ip_based [Boolean] Apply rate limits per IP address (default: true)
|
|
47
|
+
def initialize(per_second:, burst:, ip_based: true)
|
|
48
|
+
@per_second = per_second
|
|
49
|
+
@burst = burst
|
|
50
|
+
@ip_based = ip_based
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# JWT authentication configuration.
|
|
55
|
+
#
|
|
56
|
+
# Validates JWT tokens using the specified secret and algorithm.
|
|
57
|
+
# Tokens are expected in the Authorization header as "Bearer <token>".
|
|
58
|
+
#
|
|
59
|
+
# Supported algorithms:
|
|
60
|
+
# - HS256, HS384, HS512 (HMAC with SHA)
|
|
61
|
+
# - RS256, RS384, RS512 (RSA signatures)
|
|
62
|
+
# - ES256, ES384, ES512 (ECDSA signatures)
|
|
63
|
+
# - PS256, PS384, PS512 (RSA-PSS signatures)
|
|
64
|
+
#
|
|
65
|
+
# @example
|
|
66
|
+
# jwt = JwtConfig.new(
|
|
67
|
+
# secret: 'your-secret-key',
|
|
68
|
+
# algorithm: 'HS256',
|
|
69
|
+
# audience: ['api.example.com'],
|
|
70
|
+
# issuer: 'auth.example.com',
|
|
71
|
+
# leeway: 30
|
|
72
|
+
# )
|
|
73
|
+
class JwtConfig
|
|
74
|
+
attr_accessor :secret, :algorithm, :audience, :issuer, :leeway
|
|
75
|
+
|
|
76
|
+
# @param secret [String] Secret key for JWT validation
|
|
77
|
+
# @param algorithm [String] JWT algorithm (default: "HS256")
|
|
78
|
+
# @param audience [Array<String>, nil] Expected audience claim(s)
|
|
79
|
+
# @param issuer [String, nil] Expected issuer claim
|
|
80
|
+
# @param leeway [Integer] Time leeway in seconds for exp/nbf/iat claims (default: 0)
|
|
81
|
+
def initialize(secret:, algorithm: 'HS256', audience: nil, issuer: nil, leeway: 0)
|
|
82
|
+
@secret = secret
|
|
83
|
+
@algorithm = algorithm
|
|
84
|
+
@audience = audience
|
|
85
|
+
@issuer = issuer
|
|
86
|
+
@leeway = leeway
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# API key authentication configuration.
|
|
91
|
+
#
|
|
92
|
+
# Validates API keys from request headers. Keys are matched exactly.
|
|
93
|
+
#
|
|
94
|
+
# @example
|
|
95
|
+
# api_key = ApiKeyConfig.new(
|
|
96
|
+
# keys: ['key-1', 'key-2', 'key-3'],
|
|
97
|
+
# header_name: 'X-API-Key'
|
|
98
|
+
# )
|
|
99
|
+
class ApiKeyConfig
|
|
100
|
+
attr_accessor :keys, :header_name
|
|
101
|
+
|
|
102
|
+
# @param keys [Array<String>] List of valid API keys
|
|
103
|
+
# @param header_name [String] HTTP header name to check for API key (default: "X-API-Key")
|
|
104
|
+
def initialize(keys:, header_name: 'X-API-Key')
|
|
105
|
+
@keys = keys
|
|
106
|
+
@header_name = header_name
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Static file serving configuration.
|
|
111
|
+
#
|
|
112
|
+
# Serves files from a directory at a given route prefix.
|
|
113
|
+
# Multiple static file configurations can be registered.
|
|
114
|
+
#
|
|
115
|
+
# @example
|
|
116
|
+
# static = StaticFilesConfig.new(
|
|
117
|
+
# directory: './public',
|
|
118
|
+
# route_prefix: '/static',
|
|
119
|
+
# index_file: true,
|
|
120
|
+
# cache_control: 'public, max-age=3600'
|
|
121
|
+
# )
|
|
122
|
+
class StaticFilesConfig
|
|
123
|
+
attr_accessor :directory, :route_prefix, :index_file, :cache_control
|
|
124
|
+
|
|
125
|
+
# @param directory [String] Directory path containing static files
|
|
126
|
+
# @param route_prefix [String] URL prefix for serving static files (e.g., "/static")
|
|
127
|
+
# @param index_file [Boolean] Serve index.html for directory requests (default: true)
|
|
128
|
+
# @param cache_control [String, nil] Optional Cache-Control header value (e.g., "public, max-age=3600")
|
|
129
|
+
def initialize(directory:, route_prefix:, index_file: true, cache_control: nil)
|
|
130
|
+
@directory = directory
|
|
131
|
+
@route_prefix = route_prefix
|
|
132
|
+
@index_file = index_file
|
|
133
|
+
@cache_control = cache_control
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Contact information for OpenAPI documentation.
|
|
138
|
+
#
|
|
139
|
+
# @example
|
|
140
|
+
# contact = ContactInfo.new(
|
|
141
|
+
# name: 'API Team',
|
|
142
|
+
# email: 'api@example.com',
|
|
143
|
+
# url: 'https://example.com'
|
|
144
|
+
# )
|
|
145
|
+
class ContactInfo
|
|
146
|
+
attr_accessor :name, :email, :url
|
|
147
|
+
|
|
148
|
+
# @param name [String, nil] Name of the contact person/organization
|
|
149
|
+
# @param email [String, nil] Email address for contact
|
|
150
|
+
# @param url [String, nil] URL for contact information
|
|
151
|
+
def initialize(name: nil, email: nil, url: nil)
|
|
152
|
+
@name = name
|
|
153
|
+
@email = email
|
|
154
|
+
@url = url
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# License information for OpenAPI documentation.
|
|
159
|
+
#
|
|
160
|
+
# @example
|
|
161
|
+
# license = LicenseInfo.new(
|
|
162
|
+
# name: 'MIT',
|
|
163
|
+
# url: 'https://opensource.org/licenses/MIT'
|
|
164
|
+
# )
|
|
165
|
+
class LicenseInfo
|
|
166
|
+
attr_accessor :name, :url
|
|
167
|
+
|
|
168
|
+
# @param name [String] License name (e.g., "MIT", "Apache 2.0")
|
|
169
|
+
# @param url [String, nil] URL to the full license text
|
|
170
|
+
def initialize(name:, url: nil)
|
|
171
|
+
@name = name
|
|
172
|
+
@url = url
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Server information for OpenAPI documentation.
|
|
177
|
+
#
|
|
178
|
+
# Multiple servers can be specified for different environments.
|
|
179
|
+
#
|
|
180
|
+
# @example
|
|
181
|
+
# server = ServerInfo.new(
|
|
182
|
+
# url: 'https://api.example.com',
|
|
183
|
+
# description: 'Production'
|
|
184
|
+
# )
|
|
185
|
+
class ServerInfo
|
|
186
|
+
attr_accessor :url, :description
|
|
187
|
+
|
|
188
|
+
# @param url [String] Server URL (e.g., "https://api.example.com")
|
|
189
|
+
# @param description [String, nil] Description of the server (e.g., "Production", "Staging")
|
|
190
|
+
def initialize(url:, description: nil)
|
|
191
|
+
@url = url
|
|
192
|
+
@description = description
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Security scheme configuration for OpenAPI documentation.
|
|
197
|
+
#
|
|
198
|
+
# Supports HTTP (Bearer/JWT) and API Key authentication schemes.
|
|
199
|
+
#
|
|
200
|
+
# @example HTTP Bearer
|
|
201
|
+
# scheme = SecuritySchemeInfo.new(
|
|
202
|
+
# type: 'http',
|
|
203
|
+
# scheme: 'bearer',
|
|
204
|
+
# bearer_format: 'JWT'
|
|
205
|
+
# )
|
|
206
|
+
#
|
|
207
|
+
# @example API Key
|
|
208
|
+
# scheme = SecuritySchemeInfo.new(
|
|
209
|
+
# type: 'apiKey',
|
|
210
|
+
# location: 'header',
|
|
211
|
+
# name: 'X-API-Key'
|
|
212
|
+
# )
|
|
213
|
+
class SecuritySchemeInfo
|
|
214
|
+
attr_accessor :type, :scheme, :bearer_format, :location, :name
|
|
215
|
+
|
|
216
|
+
# @param type [String] Security scheme type ("http" or "apiKey")
|
|
217
|
+
# @param scheme [String, nil] HTTP scheme (e.g., "bearer", "basic") - for type="http"
|
|
218
|
+
# @param bearer_format [String, nil] Format hint for Bearer tokens (e.g., "JWT") - for type="http"
|
|
219
|
+
# @param location [String, nil] Where to look for the API key ("header", "query", or "cookie") - for type="apiKey"
|
|
220
|
+
# @param name [String, nil] Parameter name (e.g., "X-API-Key") - for type="apiKey"
|
|
221
|
+
def initialize(type:, scheme: nil, bearer_format: nil, location: nil, name: nil)
|
|
222
|
+
@type = type
|
|
223
|
+
@scheme = scheme
|
|
224
|
+
@bearer_format = bearer_format
|
|
225
|
+
@location = location
|
|
226
|
+
@name = name
|
|
227
|
+
|
|
228
|
+
validate!
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
private
|
|
232
|
+
|
|
233
|
+
def validate!
|
|
234
|
+
case @type
|
|
235
|
+
when 'http'
|
|
236
|
+
raise ArgumentError, 'scheme is required for type="http"' if @scheme.nil?
|
|
237
|
+
when 'apiKey'
|
|
238
|
+
raise ArgumentError, 'location and name are required for type="apiKey"' if @location.nil? || @name.nil?
|
|
239
|
+
else
|
|
240
|
+
raise ArgumentError, "type must be 'http' or 'apiKey', got: #{@type.inspect}"
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# OpenAPI 3.1.0 documentation configuration.
|
|
246
|
+
#
|
|
247
|
+
# Spikard can automatically generate OpenAPI documentation from your routes.
|
|
248
|
+
# When enabled, it serves:
|
|
249
|
+
# - Swagger UI at /docs (customizable)
|
|
250
|
+
# - Redoc at /redoc (customizable)
|
|
251
|
+
# - OpenAPI JSON spec at /openapi.json (customizable)
|
|
252
|
+
#
|
|
253
|
+
# Security schemes are auto-detected from middleware configuration.
|
|
254
|
+
# Schemas are generated from your route type hints and validation.
|
|
255
|
+
#
|
|
256
|
+
# @example
|
|
257
|
+
# openapi = OpenApiConfig.new(
|
|
258
|
+
# enabled: true,
|
|
259
|
+
# title: 'My API',
|
|
260
|
+
# version: '1.0.0',
|
|
261
|
+
# description: 'A great API built with Spikard',
|
|
262
|
+
# contact: ContactInfo.new(
|
|
263
|
+
# name: 'API Team',
|
|
264
|
+
# email: 'api@example.com',
|
|
265
|
+
# url: 'https://example.com'
|
|
266
|
+
# ),
|
|
267
|
+
# license: LicenseInfo.new(
|
|
268
|
+
# name: 'MIT',
|
|
269
|
+
# url: 'https://opensource.org/licenses/MIT'
|
|
270
|
+
# ),
|
|
271
|
+
# servers: [
|
|
272
|
+
# ServerInfo.new(url: 'https://api.example.com', description: 'Production'),
|
|
273
|
+
# ServerInfo.new(url: 'http://localhost:8000', description: 'Development')
|
|
274
|
+
# ]
|
|
275
|
+
# )
|
|
276
|
+
class OpenApiConfig
|
|
277
|
+
attr_accessor :enabled, :title, :version, :description,
|
|
278
|
+
:swagger_ui_path, :redoc_path, :openapi_json_path,
|
|
279
|
+
:contact, :license, :servers, :security_schemes
|
|
280
|
+
|
|
281
|
+
# @param enabled [Boolean] Enable OpenAPI generation (default: false for zero overhead)
|
|
282
|
+
# @param title [String] API title (default: "API")
|
|
283
|
+
# @param version [String] API version (default: "1.0.0")
|
|
284
|
+
# @param description [String, nil] API description (supports Markdown)
|
|
285
|
+
# @param swagger_ui_path [String] Path to serve Swagger UI (default: "/docs")
|
|
286
|
+
# @param redoc_path [String] Path to serve Redoc (default: "/redoc")
|
|
287
|
+
# @param openapi_json_path [String] Path to serve OpenAPI JSON spec (default: "/openapi.json")
|
|
288
|
+
# @param contact [ContactInfo, nil] Contact information for the API
|
|
289
|
+
# @param license [LicenseInfo, nil] License information for the API
|
|
290
|
+
# @param servers [Array<ServerInfo>] List of server URLs for different environments (default: [])
|
|
291
|
+
# @param security_schemes [Hash<String, SecuritySchemeInfo>] Custom security schemes (auto-detected if not provided)
|
|
292
|
+
def initialize(
|
|
293
|
+
enabled: false,
|
|
294
|
+
title: 'API',
|
|
295
|
+
version: '1.0.0',
|
|
296
|
+
description: nil,
|
|
297
|
+
swagger_ui_path: '/docs',
|
|
298
|
+
redoc_path: '/redoc',
|
|
299
|
+
openapi_json_path: '/openapi.json',
|
|
300
|
+
contact: nil,
|
|
301
|
+
license: nil,
|
|
302
|
+
servers: [],
|
|
303
|
+
security_schemes: {}
|
|
304
|
+
)
|
|
305
|
+
@enabled = enabled
|
|
306
|
+
@title = title
|
|
307
|
+
@version = version
|
|
308
|
+
@description = description
|
|
309
|
+
@swagger_ui_path = swagger_ui_path
|
|
310
|
+
@redoc_path = redoc_path
|
|
311
|
+
@openapi_json_path = openapi_json_path
|
|
312
|
+
@contact = contact
|
|
313
|
+
@license = license
|
|
314
|
+
@servers = servers
|
|
315
|
+
@security_schemes = security_schemes
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Complete server configuration for Spikard.
|
|
320
|
+
#
|
|
321
|
+
# This is the main configuration object that controls all aspects of the server
|
|
322
|
+
# including network settings, middleware, authentication, and more.
|
|
323
|
+
#
|
|
324
|
+
# @example
|
|
325
|
+
# config = ServerConfig.new(
|
|
326
|
+
# host: '0.0.0.0',
|
|
327
|
+
# port: 8080,
|
|
328
|
+
# workers: 4,
|
|
329
|
+
# compression: CompressionConfig.new(quality: 9),
|
|
330
|
+
# rate_limit: RateLimitConfig.new(per_second: 100, burst: 200),
|
|
331
|
+
# static_files: [
|
|
332
|
+
# StaticFilesConfig.new(
|
|
333
|
+
# directory: './public',
|
|
334
|
+
# route_prefix: '/static'
|
|
335
|
+
# )
|
|
336
|
+
# ],
|
|
337
|
+
# openapi: OpenApiConfig.new(
|
|
338
|
+
# enabled: true,
|
|
339
|
+
# title: 'My API',
|
|
340
|
+
# version: '1.0.0'
|
|
341
|
+
# )
|
|
342
|
+
# )
|
|
343
|
+
class ServerConfig
|
|
344
|
+
attr_accessor :host, :port, :workers,
|
|
345
|
+
:enable_request_id, :max_body_size, :request_timeout,
|
|
346
|
+
:compression, :rate_limit, :jwt_auth, :api_key_auth,
|
|
347
|
+
:static_files, :graceful_shutdown, :shutdown_timeout,
|
|
348
|
+
:openapi
|
|
349
|
+
|
|
350
|
+
# @param host [String] Host address to bind to (default: "127.0.0.1")
|
|
351
|
+
# @param port [Integer] Port number to listen on (default: 8000, range: 1-65535)
|
|
352
|
+
# @param workers [Integer] Number of worker processes (default: 1)
|
|
353
|
+
# @param enable_request_id [Boolean] Add X-Request-ID header to responses (default: true)
|
|
354
|
+
# @param max_body_size [Integer, nil] Maximum request body size in bytes (default: 10MB, nil for unlimited)
|
|
355
|
+
# @param request_timeout [Integer, nil] Request timeout in seconds (default: 30, nil for no timeout)
|
|
356
|
+
# @param compression [CompressionConfig, nil] Response compression configuration (default: enabled with defaults)
|
|
357
|
+
# @param rate_limit [RateLimitConfig, nil] Rate limiting configuration (default: nil/disabled)
|
|
358
|
+
# @param jwt_auth [JwtConfig, nil] JWT authentication configuration (default: nil/disabled)
|
|
359
|
+
# @param api_key_auth [ApiKeyConfig, nil] API key authentication configuration (default: nil/disabled)
|
|
360
|
+
# @param static_files [Array<StaticFilesConfig>] List of static file serving configurations (default: [])
|
|
361
|
+
# @param graceful_shutdown [Boolean] Enable graceful shutdown (default: true)
|
|
362
|
+
# @param shutdown_timeout [Integer] Graceful shutdown timeout in seconds (default: 30)
|
|
363
|
+
# @param openapi [OpenApiConfig, nil] OpenAPI configuration (default: nil/disabled)
|
|
364
|
+
def initialize(
|
|
365
|
+
host: '127.0.0.1',
|
|
366
|
+
port: 8000,
|
|
367
|
+
workers: 1,
|
|
368
|
+
enable_request_id: true,
|
|
369
|
+
max_body_size: 10 * 1024 * 1024, # 10MB
|
|
370
|
+
request_timeout: 30,
|
|
371
|
+
compression: CompressionConfig.new,
|
|
372
|
+
rate_limit: nil,
|
|
373
|
+
jwt_auth: nil,
|
|
374
|
+
api_key_auth: nil,
|
|
375
|
+
static_files: [],
|
|
376
|
+
graceful_shutdown: true,
|
|
377
|
+
shutdown_timeout: 30,
|
|
378
|
+
openapi: nil
|
|
379
|
+
)
|
|
380
|
+
@host = host
|
|
381
|
+
@port = port
|
|
382
|
+
@workers = workers
|
|
383
|
+
@enable_request_id = enable_request_id
|
|
384
|
+
@max_body_size = max_body_size
|
|
385
|
+
@request_timeout = request_timeout
|
|
386
|
+
@compression = compression
|
|
387
|
+
@rate_limit = rate_limit
|
|
388
|
+
@jwt_auth = jwt_auth
|
|
389
|
+
@api_key_auth = api_key_auth
|
|
390
|
+
@static_files = static_files
|
|
391
|
+
@graceful_shutdown = graceful_shutdown
|
|
392
|
+
@shutdown_timeout = shutdown_timeout
|
|
393
|
+
@openapi = openapi
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|