summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-01-15 15:53:17 +0530
committerBobby <[email protected]>2026-01-15 15:53:17 +0530
commitc8d0bbb5b54f5cec3ebb245f9a21d8a94b3bd944 (patch)
tree6a5c2500da90253ad07a0d5192071bb77f093d36
downloadcafe-c8d0bbb5b54f5cec3ebb245f9a21d8a94b3bd944.tar.xz
cafe-c8d0bbb5b54f5cec3ebb245f9a21d8a94b3bd944.zip
Add initial project structure with Go Fiber framework and environment configuration
-rw-r--r--.gitignore44
-rw-r--r--Makefile44
-rw-r--r--cafe/main.go33
-rw-r--r--config/config.go22
-rw-r--r--config/env.go8
-rw-r--r--go.mod22
-rw-r--r--go.sum29
-rw-r--r--router/base.go16
-rw-r--r--router/router.go22
-rw-r--r--types/http.go13
-rw-r--r--utils/env/functions.go97
-rw-r--r--utils/env/parser.go28
-rw-r--r--utils/env/setters.go107
-rw-r--r--utils/env/validators.go15
-rw-r--r--utils/urls/attach.go38
-rw-r--r--utils/urls/namespace.go7
-rw-r--r--utils/urls/path.go51
-rw-r--r--utils/urls/registery.go28
18 files changed, 624 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1cc003b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,44 @@
+# Binaries
+bin/
+tmp/
+*.exe
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool
+*.out
+
+# Dependency directories
+vendor/
+
+# Go workspace file
+go.work
+
+# Environment variables
+.env
+.env.local
+.env.dev
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+logs/
+
+# Database
+*.db
+*.sqlite
+*.sqlite3
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9a00c39
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,44 @@
+BINARY_NAME=cafe
+BUILD_PATH=bin/$(BINARY_NAME)
+MAIN_PATH=$(BINARY_NAME)/main.go
+
+.PHONY: setup clean tidy build run dev test all
+
+setup:
+ @echo "Setting up environment..."
+ @go mod download
+ @go mod tidy
+ @echo "Environment setup complete."
+
+clean:
+ @echo "Cleaning up..."
+ @rm -rf bin
+ @rm -rf tmp
+ @echo "Cleanup complete."
+
+tidy:
+ @echo "Tidying modules..."
+ @go mod tidy
+ @echo "Modules tidied."
+
+build:
+ @echo "Building..."
+ @go build -o $(BUILD_PATH) $(MAIN_PATH) || true
+ @echo "Build complete."
+
+run:
+ @if [ ! -f $(BUILD_PATH) ]; then echo "Binary not found. Building binary..."; $(MAKE) -s build; fi
+ @echo "Running..."
+ @$(BUILD_PATH) || true
+
+dev:
+ @echo "Running in development mode..."
+ @go run $(MAIN_PATH) || true
+
+test:
+ @echo "Running tests..."
+ @go test -v ./... || true
+
+all: setup clean build run
+
+.SILENT: \ No newline at end of file
diff --git a/cafe/main.go b/cafe/main.go
new file mode 100644
index 0000000..66b1860
--- /dev/null
+++ b/cafe/main.go
@@ -0,0 +1,33 @@
+package main
+
+import (
+ "cafe/config"
+ "cafe/router"
+ "fmt"
+ "log"
+
+ "github.com/gofiber/fiber/v2"
+ "github.com/gofiber/fiber/v2/middleware/cors"
+ "github.com/gofiber/fiber/v2/middleware/helmet"
+ "github.com/gofiber/fiber/v2/middleware/logger"
+ "github.com/gofiber/fiber/v2/middleware/recover"
+)
+
+func main() {
+ app := fiber.New(fiber.Config{
+ ErrorHandler: router.ErrorHandler,
+ })
+
+ app.Use(logger.New())
+ app.Use(recover.New())
+ app.Use(helmet.New(helmet.Config{
+ CrossOriginEmbedderPolicy: "unsafe-none",
+ }))
+ app.Use(cors.New())
+
+ router.Initialize(app)
+
+ address := fmt.Sprintf("%s:%d", config.Server.Host, config.Server.Port)
+ log.Printf("Starting server at %s\n", address)
+ log.Fatal(app.Listen(address))
+}
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..85f947c
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,22 @@
+package config
+
+import (
+ "cafe/utils/env"
+ "log"
+
+ "github.com/joho/godotenv"
+)
+
+var (
+ Server server
+)
+
+func init() {
+ if err := godotenv.Load(); err != nil {
+ log.Println("No .env file found, using environment variables")
+ }
+
+ if err := env.Parse(&Server); err != nil {
+ log.Fatalf("Failed to parse ServerConfig: %v", err)
+ }
+}
diff --git a/config/env.go b/config/env.go
new file mode 100644
index 0000000..bdac2b4
--- /dev/null
+++ b/config/env.go
@@ -0,0 +1,8 @@
+package config
+
+type server struct {
+ Host string `env:"SERVER_HOST" default:"localhost"`
+ Port int `env:"SERVER_PORT" default:"8080"`
+ AppSecret string `env:"APP_SECRET" default:"mysecret"`
+ DevMode bool `env:"DEV_MODE" default:"true"`
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..dd53fd2
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,22 @@
+module cafe
+
+go 1.25.5
+
+require (
+ github.com/gofiber/fiber/v2 v2.52.10
+ github.com/joho/godotenv v1.5.1
+)
+
+require (
+ github.com/andybalholm/brotli v1.1.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/klauspost/compress v1.17.9 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-runewidth v0.0.16 // indirect
+ github.com/rivo/uniseg v0.2.0 // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/valyala/fasthttp v1.51.0 // indirect
+ github.com/valyala/tcplisten v1.0.0 // indirect
+ golang.org/x/sys v0.28.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..5b70bcc
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,29 @@
+github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
+github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
+github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
+github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
+github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
+github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
+github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
diff --git a/router/base.go b/router/base.go
new file mode 100644
index 0000000..f453ba2
--- /dev/null
+++ b/router/base.go
@@ -0,0 +1,16 @@
+package router
+
+import (
+ "cafe/types"
+ "cafe/utils/urls"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+func init() {
+ urls.SetNamespace("")
+
+ urls.Path(types.GET, "/", func(c *fiber.Ctx) error {
+ return c.SendString("Welcome to Shifoo's Cafe")
+ }, "home")
+}
diff --git a/router/router.go b/router/router.go
new file mode 100644
index 0000000..c44d6d3
--- /dev/null
+++ b/router/router.go
@@ -0,0 +1,22 @@
+package router
+
+import (
+ "cafe/utils/urls"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+func Initialize(router *fiber.App) {
+ router.Static("/static", "./static")
+
+ urls.Attach(router)
+}
+
+func ErrorHandler(ctx *fiber.Ctx, err error) error {
+ code := fiber.StatusInternalServerError
+ if e, ok := err.(*fiber.Error); ok {
+ code = e.Code
+ }
+
+ return ctx.Status(code).SendString(err.Error())
+}
diff --git a/types/http.go b/types/http.go
new file mode 100644
index 0000000..bc85d99
--- /dev/null
+++ b/types/http.go
@@ -0,0 +1,13 @@
+package types
+
+type HTTPMethod string
+
+const (
+ GET HTTPMethod = "GET"
+ POST HTTPMethod = "POST"
+ PUT HTTPMethod = "PUT"
+ PATCH HTTPMethod = "PATCH"
+ DELETE HTTPMethod = "DELETE"
+ OPTIONS HTTPMethod = "OPTIONS"
+ HEAD HTTPMethod = "HEAD"
+)
diff --git a/utils/env/functions.go b/utils/env/functions.go
new file mode 100644
index 0000000..de03793
--- /dev/null
+++ b/utils/env/functions.go
@@ -0,0 +1,97 @@
+package env
+
+import (
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+func getEnv(key, defaultVal string) string {
+ if value := os.Getenv(key); value != "" {
+ return value
+ }
+ return defaultVal
+}
+
+func getEnvBool(key string, defaultVal bool) bool {
+ if value := os.Getenv(key); value != "" {
+ if parsed, err := strconv.ParseBool(value); err == nil {
+ return parsed
+ }
+ }
+ return defaultVal
+}
+
+func getEnvDuration(key string, defaultVal time.Duration) time.Duration {
+ if value := os.Getenv(key); value != "" {
+ if parsed, err := time.ParseDuration(value); err == nil {
+ return parsed
+ }
+ }
+ return defaultVal
+}
+
+func getEnvInt(key string, defaultVal int64) int64 {
+ if value := os.Getenv(key); value != "" {
+ if parsed, err := strconv.ParseInt(value, 10, 64); err == nil {
+ return parsed
+ }
+ }
+ return defaultVal
+}
+
+func getEnvFloat(key string, defaultVal float64) float64 {
+ if value := os.Getenv(key); value != "" {
+ if parsed, err := strconv.ParseFloat(value, 64); err == nil {
+ return parsed
+ }
+ }
+ return defaultVal
+}
+
+func getEnvStringSlice(key string, defaultVal []string) []string {
+ if value := os.Getenv(key); value != "" {
+ parts := strings.Split(value, ",")
+ result := make([]string, 0, len(parts))
+ for _, part := range parts {
+ trimmed := strings.TrimSpace(part)
+ if trimmed != "" {
+ result = append(result, trimmed)
+ }
+ }
+ return result
+ }
+ return defaultVal
+}
+
+func Defaults[T any](config *T) *T {
+ v := reflect.ValueOf(config)
+ if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
+ return config
+ }
+
+ elem := v.Elem()
+ t := elem.Type()
+ newStruct := reflect.New(t)
+ newElem := newStruct.Elem()
+
+ for i := range elem.NumField() {
+ field := newElem.Field(i)
+ fieldType := t.Field(i)
+
+ if !field.CanSet() {
+ continue
+ }
+
+ defaultVal := fieldType.Tag.Get("default")
+ if defaultVal == "" {
+ continue
+ }
+
+ setFieldDefault(field, defaultVal)
+ }
+
+ return newStruct.Interface().(*T)
+}
diff --git a/utils/env/parser.go b/utils/env/parser.go
new file mode 100644
index 0000000..c1fdb53
--- /dev/null
+++ b/utils/env/parser.go
@@ -0,0 +1,28 @@
+package env
+
+func Parse(config any) error {
+ elem, t, err := validateConfigInput(config)
+ if err != nil {
+ return err
+ }
+
+ for i := range elem.NumField() {
+ field := elem.Field(i)
+ fieldType := t.Field(i)
+
+ if !field.CanSet() {
+ continue
+ }
+
+ envKey := fieldType.Tag.Get("env")
+ defaultVal := fieldType.Tag.Get("default")
+
+ if envKey == "" {
+ continue
+ }
+
+ setFieldFromEnv(field, envKey, defaultVal)
+ }
+
+ return nil
+}
diff --git a/utils/env/setters.go b/utils/env/setters.go
new file mode 100644
index 0000000..7e42274
--- /dev/null
+++ b/utils/env/setters.go
@@ -0,0 +1,107 @@
+package env
+
+import (
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+func setFieldFromEnv(field reflect.Value, envKey, defaultVal string) {
+ switch field.Kind() {
+ case reflect.String:
+ field.SetString(getEnv(envKey, defaultVal))
+ case reflect.Bool:
+ defaultBool, _ := strconv.ParseBool(defaultVal)
+ field.SetBool(getEnvBool(envKey, defaultBool))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ defaultInt, _ := strconv.ParseInt(defaultVal, 10, 64)
+ field.SetInt(getEnvInt(envKey, defaultInt))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ defaultUint, _ := strconv.ParseUint(defaultVal, 10, 64)
+ setUintField(field, envKey, defaultUint)
+ case reflect.Float32, reflect.Float64:
+ defaultFloat, _ := strconv.ParseFloat(defaultVal, 64)
+ field.SetFloat(getEnvFloat(envKey, defaultFloat))
+ case reflect.Slice:
+ setSliceField(field, envKey, defaultVal)
+ default:
+ setDurationField(field, envKey, defaultVal)
+ }
+}
+
+func setUintField(field reflect.Value, envKey string, defaultVal uint64) {
+ if value := os.Getenv(envKey); value != "" {
+ if parsed, err := strconv.ParseUint(value, 10, 64); err == nil {
+ field.SetUint(parsed)
+ return
+ }
+ }
+ field.SetUint(defaultVal)
+}
+
+func setDurationField(field reflect.Value, envKey, defaultVal string) {
+ if field.Type() == reflect.TypeFor[time.Duration]() {
+ defaultDuration, _ := time.ParseDuration(defaultVal)
+ field.Set(reflect.ValueOf(getEnvDuration(envKey, defaultDuration)))
+ }
+}
+
+func setSliceField(field reflect.Value, envKey, defaultVal string) {
+ if field.Type().Elem().Kind() == reflect.String {
+ var defaultSlice []string
+ if defaultVal != "" {
+ parts := strings.Split(defaultVal, ",")
+ for _, part := range parts {
+ trimmed := strings.TrimSpace(part)
+ if trimmed != "" {
+ defaultSlice = append(defaultSlice, trimmed)
+ }
+ }
+ }
+ result := getEnvStringSlice(envKey, defaultSlice)
+ field.Set(reflect.ValueOf(result))
+ }
+}
+
+func setFieldDefault(field reflect.Value, defaultVal string) {
+ switch field.Kind() {
+ case reflect.String:
+ field.SetString(defaultVal)
+ case reflect.Bool:
+ if defaultBool, err := strconv.ParseBool(defaultVal); err == nil {
+ field.SetBool(defaultBool)
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ if defaultInt, err := strconv.ParseInt(defaultVal, 10, 64); err == nil {
+ field.SetInt(defaultInt)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ if defaultUint, err := strconv.ParseUint(defaultVal, 10, 64); err == nil {
+ field.SetUint(defaultUint)
+ }
+ case reflect.Float32, reflect.Float64:
+ if defaultFloat, err := strconv.ParseFloat(defaultVal, 64); err == nil {
+ field.SetFloat(defaultFloat)
+ }
+ case reflect.Slice:
+ if field.Type().Elem().Kind() == reflect.String && defaultVal != "" {
+ parts := strings.Split(defaultVal, ",")
+ result := make([]string, 0, len(parts))
+ for _, part := range parts {
+ trimmed := strings.TrimSpace(part)
+ if trimmed != "" {
+ result = append(result, trimmed)
+ }
+ }
+ field.Set(reflect.ValueOf(result))
+ }
+ default:
+ if field.Type() == reflect.TypeFor[time.Duration]() {
+ if defaultDuration, err := time.ParseDuration(defaultVal); err == nil {
+ field.Set(reflect.ValueOf(defaultDuration))
+ }
+ }
+ }
+}
diff --git a/utils/env/validators.go b/utils/env/validators.go
new file mode 100644
index 0000000..fa9b17f
--- /dev/null
+++ b/utils/env/validators.go
@@ -0,0 +1,15 @@
+package env
+
+import (
+ "fmt"
+ "reflect"
+)
+
+func validateConfigInput(config any) (reflect.Value, reflect.Type, error) {
+ v := reflect.ValueOf(config)
+ if v.Kind() != reflect.Pointer || v.Elem().Kind() != reflect.Struct {
+ return reflect.Value{}, nil, fmt.Errorf("config must be a pointer to struct")
+ }
+ elem := v.Elem()
+ return elem, elem.Type(), nil
+}
diff --git a/utils/urls/attach.go b/utils/urls/attach.go
new file mode 100644
index 0000000..7d5d732
--- /dev/null
+++ b/utils/urls/attach.go
@@ -0,0 +1,38 @@
+package urls
+
+import (
+ "cafe/types"
+ "log"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+var methodBinders = map[types.HTTPMethod]func(fiber.Router, string, fiber.Handler) fiber.Router{
+ types.GET: func(r fiber.Router, path string, h fiber.Handler) fiber.Router { return r.Get(path, h) },
+ types.POST: func(r fiber.Router, path string, h fiber.Handler) fiber.Router { return r.Post(path, h) },
+ types.PUT: func(r fiber.Router, path string, h fiber.Handler) fiber.Router { return r.Put(path, h) },
+ types.PATCH: func(r fiber.Router, path string, h fiber.Handler) fiber.Router { return r.Patch(path, h) },
+ types.DELETE: func(r fiber.Router, path string, h fiber.Handler) fiber.Router { return r.Delete(path, h) },
+ types.OPTIONS: func(r fiber.Router, path string, h fiber.Handler) fiber.Router { return r.Options(path, h) },
+ types.HEAD: func(r fiber.Router, path string, h fiber.Handler) fiber.Router { return r.Head(path, h) },
+}
+
+func Attach(app *fiber.App) {
+ namespaceGroups := make(map[string]fiber.Router)
+
+ for fullName, route := range registry.routes {
+ group, exists := namespaceGroups[route.namespace]
+ if !exists {
+ group = app.Group("/" + route.namespace)
+ namespaceGroups[route.namespace] = group
+ }
+
+ binder, ok := methodBinders[route.method]
+ if !ok {
+ log.Fatalf("%s", "unsupported HTTP method: "+string(route.method))
+ }
+
+ fiberRoute := binder(group, route.path, route.handler)
+ fiberRoute.Name(fullName)
+ }
+}
diff --git a/utils/urls/namespace.go b/utils/urls/namespace.go
new file mode 100644
index 0000000..7bb5311
--- /dev/null
+++ b/utils/urls/namespace.go
@@ -0,0 +1,7 @@
+package urls
+
+func SetNamespace(namespace string) {
+ registry.mutex.Lock()
+ defer registry.mutex.Unlock()
+ registry.currentNamespace = namespace
+}
diff --git a/utils/urls/path.go b/utils/urls/path.go
new file mode 100644
index 0000000..e24ed58
--- /dev/null
+++ b/utils/urls/path.go
@@ -0,0 +1,51 @@
+package urls
+
+import (
+ "cafe/types"
+ "strings"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+func Path(method types.HTTPMethod, path string, handler fiber.Handler, name string) {
+ registry.mutex.Lock()
+ defer registry.mutex.Unlock()
+
+ namespace := registry.currentNamespace
+ fullName := name
+ fullPath := path
+
+ if namespace != "" {
+ if !strings.HasPrefix(path, "/") {
+ path = "/" + path
+ }
+
+ fullName = namespace + "." + name
+ fullPath = "/" + namespace + path
+ } else {
+ if !strings.HasPrefix(fullPath, "/") {
+ fullPath = "/" + fullPath
+ }
+ }
+
+ registry.routes[fullName] = registeredRoute{
+ method: method,
+ path: path,
+ handler: handler,
+ namespace: namespace,
+ name: name,
+ fullPath: fullPath,
+ }
+}
+
+func GetFullPath(routeName string) (string, bool) {
+ registry.mutex.Lock()
+ defer registry.mutex.Unlock()
+
+ route, ok := registry.routes[routeName]
+ if !ok {
+ return "", false
+ }
+
+ return route.fullPath, true
+}
diff --git a/utils/urls/registery.go b/utils/urls/registery.go
new file mode 100644
index 0000000..b4e6a36
--- /dev/null
+++ b/utils/urls/registery.go
@@ -0,0 +1,28 @@
+package urls
+
+import (
+ "sync"
+
+ "cafe/types"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+type registeredRoute struct {
+ method types.HTTPMethod
+ path string
+ handler fiber.Handler
+ namespace string
+ name string
+ fullPath string
+}
+
+type routeRegistry struct {
+ mutex sync.Mutex
+ currentNamespace string
+ routes map[string]registeredRoute
+}
+
+var registry = &routeRegistry{
+ routes: make(map[string]registeredRoute),
+}