├── .gitignore ├── README.md ├── examples └── main.go ├── flash.go ├── go.mod └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | flash.iml 3 | .DS_Store 4 | .history -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Set flash message for routes. 2 | 3 | This package is build to send the flash messages on the top of Gofiber 4 | 5 | ## Installation 6 | The package can be used to validate the data and send flash message to other route. 7 | > go get github.com/sujit-baniya/flash 8 | 9 | 10 | ## Usage 11 | 12 | ```go 13 | package main 14 | 15 | import ( 16 | "github.com/gofiber/fiber/v2" 17 | "github.com/sujit-baniya/flash" 18 | ) 19 | 20 | func main() { 21 | app := fiber.New() 22 | app.Get("/success-redirect", func(c *fiber.Ctx) error { 23 | return c.JSON(flash.Get(c)) 24 | }) 25 | 26 | app.Get("/error-redirect", func(c *fiber.Ctx) error { 27 | flash.Get(c) 28 | return c.JSON(flash.Get(c)) 29 | }) 30 | 31 | app.Get("/error", func(c *fiber.Ctx) error { 32 | mp := fiber.Map{ 33 | "error": true, 34 | "message": "I'm receiving error with inline error data", 35 | } 36 | return flash.WithError(c, mp).Redirect("/error-redirect") 37 | }) 38 | 39 | app.Get("/success", func(c *fiber.Ctx) error { 40 | mp := fiber.Map{ 41 | "success": true, 42 | "message": "I'm receiving success with inline success data", 43 | } 44 | return flash.WithSuccess(c, mp).Redirect("/success-redirect") 45 | }) 46 | 47 | app.Get("/data", func(c *fiber.Ctx) error { 48 | mp := fiber.Map{ 49 | "text": "Received arbitrary data", 50 | } 51 | return flash.WithData(c, mp).Redirect("/success-redirect") 52 | }) 53 | 54 | app.Listen(":8080") 55 | } 56 | 57 | ``` 58 | -------------------------------------------------------------------------------- /examples/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gofiber/fiber/v2" 5 | "github.com/sujit-baniya/flash" 6 | ) 7 | 8 | func main() { 9 | app := fiber.New() 10 | app.Get("/success-redirect", func(c *fiber.Ctx) error { 11 | return c.JSON(flash.Get(c)) 12 | }) 13 | 14 | app.Get("/error-redirect", func(c *fiber.Ctx) error { 15 | flash.Get(c) 16 | return c.JSON(flash.Get(c)) 17 | }) 18 | 19 | app.Get("/error", func(c *fiber.Ctx) error { 20 | mp := fiber.Map{ 21 | "error": true, 22 | "message": "I'm receiving error with inline error data", 23 | } 24 | return flash.WithError(c, mp).Redirect("/error-redirect") 25 | }) 26 | 27 | app.Get("/success", func(c *fiber.Ctx) error { 28 | mp := fiber.Map{ 29 | "success": true, 30 | "message": "I'm receiving success with inline success data", 31 | } 32 | return flash.WithSuccess(c, mp).Redirect("/success-redirect") 33 | }) 34 | 35 | app.Get("/data", func(c *fiber.Ctx) error { 36 | mp := fiber.Map{ 37 | "text": "Received arbitrary data", 38 | } 39 | return flash.WithData(c, mp).Redirect("/success-redirect") 40 | }) 41 | 42 | app.Listen(":8080") 43 | } 44 | -------------------------------------------------------------------------------- /flash.go: -------------------------------------------------------------------------------- 1 | package flash 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "regexp" 7 | "time" 8 | 9 | "github.com/gofiber/fiber/v2" 10 | ) 11 | 12 | type Flash struct { 13 | data fiber.Map 14 | config Config 15 | } 16 | 17 | type Config struct { 18 | Name string `json:"name"` 19 | Value string `json:"value"` 20 | Path string `json:"path"` 21 | Domain string `json:"domain"` 22 | MaxAge int `json:"max_age"` 23 | Expires time.Time `json:"expires"` 24 | Secure bool `json:"secure"` 25 | HTTPOnly bool `json:"http_only"` 26 | SameSite string `json:"same_site"` 27 | SessionOnly bool `json:"session_only"` 28 | } 29 | 30 | var DefaultFlash *Flash 31 | 32 | func init() { 33 | Default(Config{ 34 | Name: "fiber-app-flash", 35 | }) 36 | } 37 | 38 | var cookieKeyValueParser = regexp.MustCompile("\x00([^:]*):([^\x00]*)\x00") 39 | 40 | func Default(config Config) { 41 | DefaultFlash = New(config) 42 | } 43 | 44 | func New(config Config) *Flash { 45 | if config.SameSite == "" { 46 | config.SameSite = "Lax" 47 | } 48 | return &Flash{ 49 | config: config, 50 | data: fiber.Map{}, 51 | } 52 | } 53 | 54 | func (f *Flash) Get(c *fiber.Ctx) fiber.Map { 55 | t := fiber.Map{} 56 | f.data = nil 57 | cookieValue := c.Cookies(f.config.Name) 58 | if cookieValue != "" { 59 | parseKeyValueCookie(cookieValue, func(key string, val interface{}) { 60 | t[key] = val 61 | }) 62 | f.data = t 63 | } 64 | c.Set("Set-Cookie", f.config.Name+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; HttpOnly; SameSite="+f.config.SameSite) 65 | if f.data == nil { 66 | f.data = fiber.Map{} 67 | } 68 | return f.data 69 | } 70 | 71 | func (f *Flash) Redirect(c *fiber.Ctx, location string, data interface{}, status ...int) error { 72 | f.data = data.(fiber.Map) 73 | if len(status) > 0 { 74 | return c.Redirect(location, status[0]) 75 | } else { 76 | return c.Redirect(location, fiber.StatusFound) 77 | } 78 | } 79 | 80 | func (f *Flash) RedirectToRoute(c *fiber.Ctx, routeName string, data fiber.Map, status ...int) error { 81 | f.data = data 82 | if len(status) > 0 { 83 | return c.RedirectToRoute(routeName, data, status[0]) 84 | } else { 85 | return c.RedirectToRoute(routeName, data, fiber.StatusFound) 86 | } 87 | } 88 | 89 | func (f *Flash) RedirectBack(c *fiber.Ctx, fallback string, data fiber.Map, status ...int) error { 90 | f.data = data 91 | if len(status) > 0 { 92 | return c.RedirectBack(fallback, status[0]) 93 | } else { 94 | return c.RedirectBack(fallback, fiber.StatusFound) 95 | } 96 | } 97 | 98 | func (f *Flash) WithError(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 99 | f.data = data 100 | f.error(c) 101 | return c 102 | } 103 | 104 | func (f *Flash) WithSuccess(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 105 | f.data = data 106 | f.success(c) 107 | return c 108 | } 109 | 110 | func (f *Flash) WithWarn(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 111 | f.data = data 112 | f.warn(c) 113 | return c 114 | } 115 | 116 | func (f *Flash) WithInfo(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 117 | f.data = data 118 | f.info(c) 119 | return c 120 | } 121 | 122 | func (f *Flash) WithData(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 123 | f.data = data 124 | f.setCookie(c) 125 | return c 126 | } 127 | 128 | func (f *Flash) error(c *fiber.Ctx) { 129 | f.data["error"] = true 130 | f.setCookie(c) 131 | } 132 | 133 | func (f *Flash) success(c *fiber.Ctx) { 134 | f.data["success"] = true 135 | f.setCookie(c) 136 | } 137 | 138 | func (f *Flash) warn(c *fiber.Ctx) { 139 | f.data["warn"] = true 140 | f.setCookie(c) 141 | } 142 | 143 | func (f *Flash) info(c *fiber.Ctx) { 144 | f.data["info"] = true 145 | f.setCookie(c) 146 | } 147 | 148 | func (f *Flash) setCookie(c *fiber.Ctx) { 149 | var flashValue string 150 | for key, value := range f.data { 151 | flashValue += "\x00" + key + ":" + fmt.Sprintf("%v", value) + "\x00" 152 | } 153 | c.Cookie(&fiber.Cookie{ 154 | Name: f.config.Name, 155 | Value: url.QueryEscape(flashValue), 156 | SameSite: f.config.SameSite, 157 | Secure: f.config.Secure, 158 | Path: f.config.Path, 159 | Domain: f.config.Domain, 160 | MaxAge: f.config.MaxAge, 161 | Expires: f.config.Expires, 162 | HTTPOnly: f.config.HTTPOnly, 163 | SessionOnly: f.config.SessionOnly, 164 | }) 165 | } 166 | 167 | func Get(c *fiber.Ctx) fiber.Map { 168 | return DefaultFlash.Get(c) 169 | } 170 | 171 | func Redirect(c *fiber.Ctx, location string, data interface{}, status ...int) error { 172 | return DefaultFlash.Redirect(c, location, data, status...) 173 | } 174 | 175 | func RedirectToRoute(c *fiber.Ctx, routeName string, data fiber.Map, status ...int) error { 176 | return DefaultFlash.RedirectToRoute(c, routeName, data, status...) 177 | } 178 | 179 | func RedirectBack(c *fiber.Ctx, fallback string, data fiber.Map, status ...int) error { 180 | return DefaultFlash.RedirectBack(c, fallback, data, status...) 181 | } 182 | 183 | func WithError(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 184 | return DefaultFlash.WithError(c, data) 185 | } 186 | 187 | func WithSuccess(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 188 | return DefaultFlash.WithSuccess(c, data) 189 | } 190 | 191 | func WithWarn(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 192 | return DefaultFlash.WithWarn(c, data) 193 | } 194 | 195 | func WithInfo(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 196 | return DefaultFlash.WithInfo(c, data) 197 | } 198 | 199 | func WithData(c *fiber.Ctx, data fiber.Map) *fiber.Ctx { 200 | return DefaultFlash.WithData(c, data) 201 | } 202 | 203 | // parseKeyValueCookie takes the raw (escaped) cookie value and parses out key values. 204 | func parseKeyValueCookie(val string, cb func(key string, val interface{})) { 205 | val, _ = url.QueryUnescape(val) 206 | if matches := cookieKeyValueParser.FindAllStringSubmatch(val, -1); matches != nil { 207 | for _, match := range matches { 208 | cb(match[1], match[2]) 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sujit-baniya/flash 2 | 3 | go 1.22 4 | 5 | require github.com/gofiber/fiber/v2 v2.52.4 6 | 7 | require ( 8 | github.com/andybalholm/brotli v1.1.0 // indirect 9 | github.com/google/uuid v1.6.0 // indirect 10 | github.com/klauspost/compress v1.17.8 // indirect 11 | github.com/mattn/go-colorable v0.1.13 // indirect 12 | github.com/mattn/go-isatty v0.0.20 // indirect 13 | github.com/mattn/go-runewidth v0.0.15 // indirect 14 | github.com/rivo/uniseg v0.4.7 // indirect 15 | github.com/valyala/bytebufferpool v1.0.0 // indirect 16 | github.com/valyala/fasthttp v1.54.0 // indirect 17 | github.com/valyala/tcplisten v1.0.0 // indirect 18 | golang.org/x/sys v0.20.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 2 | github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 3 | github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= 4 | github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= 5 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 6 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 7 | github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= 8 | github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 9 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 10 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 11 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 12 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 13 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 14 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 15 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 16 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 17 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 18 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 19 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 20 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 21 | github.com/valyala/fasthttp v1.54.0 h1:cCL+ZZR3z3HPLMVfEYVUMtJqVaui0+gu7Lx63unHwS0= 22 | github.com/valyala/fasthttp v1.54.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM= 23 | github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= 24 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 25 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 26 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 27 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 28 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 29 | --------------------------------------------------------------------------------