Skip to content

Commit c11ca75

Browse files
committed
feat(gateway): add new gateway extension with access logging, authentication, caching, and circuit breaker
- Introduced a comprehensive gateway extension that includes: - Access logging for structured request logging. - Authentication mechanisms with support for multiple providers. - Caching capabilities for HTTP responses with configurable policies. - Circuit breaker implementation for resilience against failures. - Updated configuration structure to accommodate new gateway settings. - Enhanced existing files and examples to demonstrate the new features. This update significantly enhances the functionality and robustness of the gateway component.
1 parent bf23a23 commit c11ca75

50 files changed

Lines changed: 10647 additions & 117 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ on:
2626
- discovery
2727
- events
2828
- features
29+
- gateway
2930
- graphql
3031
- grpc
3132
- hls
@@ -227,7 +228,7 @@ jobs:
227228
if: github.event_name == 'workflow_dispatch' && steps.resolve.outputs.is_all == 'true' && steps.resolve.outputs.dry_run == 'false'
228229
run: |
229230
VERSION="${{ steps.resolve.outputs.version }}"
230-
EXTENSIONS=(ai auth cache consensus cron database discovery events features graphql grpc hls kafka mcp mqtt orpc queue search security storage streaming webrtc)
231+
EXTENSIONS=(ai auth cache consensus cron database discovery events features gateway graphql grpc hls kafka mcp mqtt orpc queue search security storage streaming webrtc)
231232
232233
for ext in "${EXTENSIONS[@]}"; do
233234
EXT_TAG="extensions/${ext}/v${VERSION}"

.release-please-manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"extensions/discovery": "0.1.0",
1010
"extensions/events": "0.1.0",
1111
"extensions/features": "0.1.0",
12+
"extensions/gateway": "0.1.0",
1213
"extensions/graphql": "0.1.0",
1314
"extensions/grpc": "0.1.0",
1415
"extensions/hls": "0.1.0",

app_impl.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
healthinternal "github.com/xraph/forge/internal/health"
1919
"github.com/xraph/forge/internal/logger"
2020
metricsinternal "github.com/xraph/forge/internal/metrics"
21+
internalrouter "github.com/xraph/forge/internal/router"
2122
"github.com/xraph/forge/internal/shared"
2223
"github.com/xraph/vessel"
2324
"gopkg.in/yaml.v3"
@@ -253,6 +254,11 @@ func newApp(config AppConfig) *app {
253254
routerOpts = append(routerOpts, WithHealth(config.HealthConfig))
254255
}
255256

257+
// Add HTTP address for automatic localhost server in OpenAPI
258+
if config.HTTPAddress != "" {
259+
routerOpts = append(routerOpts, internalrouter.WithHTTPAddress(config.HTTPAddress))
260+
}
261+
256262
router := NewRouter(routerOpts...)
257263

258264
// Register core services with DI - Both key-based (legacy) and type-based (new pattern)
@@ -1337,20 +1343,23 @@ func loadForgeYAMLConfig(config AppConfig, logger Logger) AppConfig {
13371343
}
13381344

13391345
var forgeConfigPath string
1346+
13401347
maxDepth := 5
13411348

13421349
// Search up the directory tree
1343-
for i := 0; i < maxDepth; i++ {
1350+
for range maxDepth {
13441351
yamlPath := filepath.Join(dir, ".forge.yaml")
13451352
ymlPath := filepath.Join(dir, ".forge.yml")
13461353

13471354
if _, err := os.Stat(yamlPath); err == nil {
13481355
forgeConfigPath = yamlPath
1356+
13491357
break
13501358
}
13511359

13521360
if _, err := os.Stat(ymlPath); err == nil {
13531361
forgeConfigPath = ymlPath
1362+
13541363
break
13551364
}
13561365

@@ -1360,6 +1369,7 @@ func loadForgeYAMLConfig(config AppConfig, logger Logger) AppConfig {
13601369
// Reached root
13611370
break
13621371
}
1372+
13631373
dir = parent
13641374
}
13651375

@@ -1374,6 +1384,7 @@ func loadForgeYAMLConfig(config AppConfig, logger Logger) AppConfig {
13741384
if logger != nil {
13751385
logger.Debug("failed to read .forge.yaml", F("path", forgeConfigPath), F("error", err.Error()))
13761386
}
1387+
13771388
return config
13781389
}
13791390

@@ -1382,6 +1393,7 @@ func loadForgeYAMLConfig(config AppConfig, logger Logger) AppConfig {
13821393
if logger != nil {
13831394
logger.Debug("failed to parse .forge.yaml", F("path", forgeConfigPath), F("error", err.Error()))
13841395
}
1396+
13851397
return config
13861398
}
13871399

cmd/forge/plugins/generate.go

Lines changed: 253 additions & 61 deletions
Large diffs are not rendered by default.

examples/file_upload/main.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,7 @@ func main() {
306306
Title: "File Upload API",
307307
Version: "1.0.0",
308308
Description: "Production-ready file upload API with validation, multipart forms, and security best practices",
309-
Servers: []forge.OpenAPIServer{
310-
{URL: "http://localhost:8085", Description: "Development server"},
311-
},
309+
// Note: Localhost server is automatically added based on HTTPAddress
312310
}),
313311
},
314312
})

examples/openapi-demo/main.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,8 @@ func main() {
7575
Version: "1.0.0",
7676

7777
// Server configuration
78+
// Note: Localhost server is automatically added based on HTTPAddress
7879
Servers: []forge.OpenAPIServer{
79-
{
80-
URL: "http://localhost:8080",
81-
Description: "Development server",
82-
},
8380
{
8481
URL: "https://api.example.com",
8582
Description: "Production server",

examples/openapi_complete/main.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,8 @@ func main() {
6969
Title: "User Management API",
7070
Description: "A comprehensive API demonstrating OpenAPI schema generation with Forge",
7171
Version: "1.0.0",
72+
// Note: Localhost server is automatically added based on HTTPAddress
7273
Servers: []forge.OpenAPIServer{
73-
{
74-
URL: "http://localhost:8084",
75-
Description: "Development server",
76-
},
7774
{
7875
URL: "https://api.example.com",
7976
Description: "Production server",

examples/openapi_unified/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ func main() {
188188
Title: "Unified Request Schema API",
189189
Description: "Demonstrates unified request schema with path, query, header, and body parameters in a single struct",
190190
Version: "2.0.0",
191+
// Note: Localhost server is automatically added based on HTTPAddress
191192
Servers: []forge.OpenAPIServer{
192-
{URL: "http://localhost:8085", Description: "Development server"},
193193
{URL: "https://api.example.com", Description: "Production server"},
194194
},
195195
UIPath: "/swagger",

examples/optional_tag/main.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,7 @@ func main() {
8888
Title: "Optional Tag Demo API",
8989
Description: "API demonstrating the optional tag for query parameters",
9090
Version: "1.0.0",
91-
Servers: []forge.OpenAPIServer{
92-
{
93-
URL: "http://localhost:8080",
94-
Description: "Development server",
95-
},
96-
},
91+
// Note: Localhost server is automatically added based on HTTPAddress
9792
}),
9893
},
9994
})

extensions/database/extension.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ type Extension struct {
2121
*forge.BaseExtension
2222

2323
config Config
24-
// No longer storing manager - Vessel manages it
2524
}
2625

2726
// NewExtension creates a new database extension with variadic options.
@@ -80,7 +79,6 @@ func (e *Extension) Register(app forge.App) error {
8079

8180
// loadConfiguration loads configuration from YAML files or programmatic sources.
8281
func (e *Extension) loadConfiguration() error {
83-
fmt.Println("database: loading configuration", e.config.Databases)
8482
programmaticConfig := e.config
8583
hasProgrammaticDatabases := e.hasProgrammaticDatabases(programmaticConfig)
8684

@@ -129,7 +127,6 @@ func (e *Extension) tryLoadFromConfigFile() (Config, bool) {
129127
// Try "extensions.database" first (namespaced pattern)
130128
if cm.IsSet("extensions.database") {
131129
if err := cm.Bind("extensions.database", &finalConfig); err == nil {
132-
fmt.Println("database: loaded from config file", finalConfig.Databases[0].DSN)
133130
e.Logger().Debug("database: loaded from config file",
134131
forge.F("key", "extensions.database"),
135132
forge.F("databases", len(finalConfig.Databases)),
@@ -143,7 +140,6 @@ func (e *Extension) tryLoadFromConfigFile() (Config, bool) {
143140
// Try legacy "database" key if not loaded yet
144141
if cm.IsSet("database") {
145142
if err := cm.Bind("database", &finalConfig); err == nil {
146-
fmt.Println("database: loaded from config file 2", finalConfig.Databases[0].DSN)
147143
e.Logger().Debug("database: loaded from config file",
148144
forge.F("key", "database"),
149145
forge.F("databases", len(finalConfig.Databases)),
@@ -179,15 +175,13 @@ func (e *Extension) mergeConfigurations(yamlConfig, programmaticConfig Config, h
179175
// Build a set of existing database names from YAML config
180176
existingNames := make(map[string]bool)
181177
for _, db := range yamlConfig.Databases {
182-
fmt.Println("database: merging database", db.Name, db.DSN)
183178
existingNames[db.Name] = true
184179
}
185180

186181
// Only append programmatic databases that don't already exist
187182
// YAML config takes precedence over programmatic config
188183
skipped := 0
189184
for _, db := range programmaticConfig.Databases {
190-
fmt.Println("database: merging database 2", db.Name, db.DSN)
191185
if existingNames[db.Name] {
192186
e.Logger().Warn("database: skipping duplicate programmatic database (YAML config takes precedence)",
193187
forge.F("name", db.Name))
@@ -244,7 +238,6 @@ func (e *Extension) registerDatabaseManager() error {
244238

245239
// createDatabase creates a database instance based on its type.
246240
func (e *Extension) createDatabase(config DatabaseConfig, logger forge.Logger, metrics forge.Metrics) (Database, error) {
247-
fmt.Println("database: creating database", config.Name, config.DSN)
248241
switch config.Type {
249242
case TypePostgres, TypeMySQL, TypeSQLite:
250243
return NewSQLDatabase(config, logger, metrics)
@@ -300,7 +293,6 @@ func (e *Extension) registerDefaultDatabase(defaultName string) error {
300293
// findDatabaseConfig finds a database configuration by name.
301294
func (e *Extension) findDatabaseConfig(name string) *DatabaseConfig {
302295
for i := range e.config.Databases {
303-
fmt.Println("database: finding database config", name, e.config.Databases[i].DSN)
304296
if e.config.Databases[i].Name == name {
305297
return &e.config.Databases[i]
306298
}

0 commit comments

Comments
 (0)