Skip to content

Commit 2daf191

Browse files
paskalumputun
authored andcommitted
migrate example from chi to stdlib + routegroup
- replace go-chi/chi with go-pkgz/routegroup v1.6.0 - update router initialization to use routegroup.New(http.NewServeMux()) - use Go 1.22+ method+path syntax for route definitions - update fileServer to use stdlib patterns with {file...} wildcard - mount auth handlers using http.StripPrefix - update README with routegroup as primary example, chi in collapsible section
1 parent dfe4210 commit 2daf191

7 files changed

Lines changed: 72 additions & 28 deletions

File tree

.github/workflows/ci-v2.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,21 @@ jobs:
2222
sudo systemctl stop mono-xsp4.service || true
2323
sudo systemctl disable mono-xsp4.service || true
2424
25+
- name: checkout
26+
uses: actions/checkout@v4
27+
2528
- name: set up go
2629
uses: actions/setup-go@v5
2730
with:
2831
go-version: "1.24"
32+
cache-dependency-path: v2/go.sum
2933
id: go
3034

3135
- name: launch mongodb
3236
uses: wbari/start-mongoDB@v0.2
3337
with:
3438
mongoDBVersion: "6.0"
3539

36-
- name: checkout
37-
uses: actions/checkout@v4
38-
3940
- name: build and test
4041
run: |
4142
go test -timeout=60s -v -race -p 1 -covermode=atomic -coverprofile=$GITHUB_WORKSPACE/profile.cov ./...

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ jobs:
2222
sudo systemctl stop mono-xsp4.service || true
2323
sudo systemctl disable mono-xsp4.service || true
2424
25+
- name: checkout
26+
uses: actions/checkout@v4
27+
2528
- name: set up go
2629
uses: actions/setup-go@v5
2730
with:
@@ -33,9 +36,6 @@ jobs:
3336
with:
3437
mongoDBVersion: "6.0"
3538

36-
- name: checkout
37-
uses: actions/checkout@v4
38-
3939
- name: build the example
4040
working-directory: _example
4141
run: |

.github/workflows/codeql-analysis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939

4040
# Initializes the CodeQL tools for scanning.
4141
- name: Initialize CodeQL
42-
uses: github/codeql-action/init@v2
42+
uses: github/codeql-action/init@v3
4343
with:
4444
languages: ${{ matrix.language }}
4545
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,4 +53,4 @@ jobs:
5353
TZ: "America/Chicago"
5454

5555
- name: Perform CodeQL Analysis
56-
uses: github/codeql-action/analyze@v2
56+
uses: github/codeql-action/analyze@v3

README.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,53 @@ The PR demonstrates how to handle migration for a production application with ex
6868

6969
## Usage
7070

71-
Example with chi router:
71+
Example with [routegroup](https://github.com/go-pkgz/routegroup) (stdlib-based router):
72+
73+
```go
74+
func main() {
75+
// define options
76+
options := auth.Opts{
77+
SecretReader: token.SecretFunc(func(id string) (string, error) { // secret key for JWT
78+
return "secret", nil
79+
}),
80+
TokenDuration: time.Minute * 5, // token expires in 5 minutes
81+
CookieDuration: time.Hour * 24, // cookie expires in 1 day and will enforce re-login
82+
Issuer: "my-test-app",
83+
URL: "http://127.0.0.1:8080",
84+
AvatarStore: avatar.NewLocalFS("/tmp"),
85+
Validator: token.ValidatorFunc(func(_ string, claims token.Claims) bool {
86+
// allow only dev_* names
87+
return claims.User != nil && strings.HasPrefix(claims.User.Name, "dev_")
88+
}),
89+
}
90+
91+
// create auth service with providers
92+
service := auth.NewService(options)
93+
service.AddProvider("github", "<Client ID>", "<Client Secret>") // add github provider
94+
service.AddProvider("facebook", "<Client ID>", "<Client Secret>") // add facebook provider
95+
96+
// retrieve auth middleware
97+
m := service.Middleware()
98+
99+
// setup http server
100+
router := routegroup.New(http.NewServeMux())
101+
router.HandleFunc("GET /open", openRouteHandler) // open api
102+
router.Group().Route(func(r *routegroup.Bundle) {
103+
r.Use(m.Auth)
104+
r.HandleFunc("GET /private", protectedRouteHandler) // protected api
105+
})
106+
107+
// setup auth routes
108+
authRoutes, avaRoutes := service.Handlers()
109+
router.Handle("/auth/", http.StripPrefix("/auth", authRoutes)) // add auth handlers
110+
router.Handle("/avatar/", http.StripPrefix("/avatar", avaRoutes)) // add avatar handler
111+
112+
log.Fatal(http.ListenAndServe(":8080", router))
113+
}
114+
```
115+
116+
<details>
117+
<summary>Example with chi router</summary>
72118

73119
```go
74120
func main() {
@@ -109,6 +155,7 @@ func main() {
109155
log.Fatal(http.ListenAndServe(":8080", router))
110156
}
111157
```
158+
</details>
112159

113160
## Middleware
114161

_example/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ go 1.24.0
55
replace github.com/go-pkgz/auth/v2 => ../v2
66

77
require (
8-
github.com/go-chi/chi/v5 v5.2.3
98
github.com/go-oauth2/oauth2/v4 v4.5.4
109
github.com/go-pkgz/auth/v2 v2.0.0-00010101000000-000000000000
1110
github.com/go-pkgz/lgr v0.12.1
1211
github.com/go-pkgz/rest v1.20.4
12+
github.com/go-pkgz/routegroup v1.6.0
1313
github.com/golang-jwt/jwt/v5 v5.3.0
1414
golang.org/x/oauth2 v0.33.0
1515
)

_example/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
1212
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
1313
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
1414
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
15-
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
16-
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
1715
github.com/go-oauth2/oauth2/v4 v4.5.4 h1:YjI0tmGW8oxVhn9QSBIxlr641QugWrJY5UWa6XmLcW0=
1816
github.com/go-oauth2/oauth2/v4 v4.5.4/go.mod h1:BXiOY+QZtZy2ewbsGk2B5P8TWmtz/Rf7ES5ZttQFxfQ=
1917
github.com/go-pkgz/lgr v0.12.1 h1:8GVfG2rSARq3Eaj5PP158rtBR2LHVGkwioIkQBGbvKg=
@@ -22,6 +20,8 @@ github.com/go-pkgz/repeater v1.2.0 h1:oJFvjyKdTDd5RCzpzxlzYIZFFj6Zfl17rE1aUfu6Uj
2220
github.com/go-pkgz/repeater v1.2.0/go.mod h1:vypP6xamA53MFmafnGUucqOmALKk36xgKu2hSG73LHM=
2321
github.com/go-pkgz/rest v1.20.4 h1:8ufcP1IqoDhCvIFdXPtvyX4HSS16SM6THBe2a6L0/kg=
2422
github.com/go-pkgz/rest v1.20.4/go.mod h1:2/LEZGndSxpVvExsMn48AjUgiTn6kILqjpoaRnl62JU=
23+
github.com/go-pkgz/routegroup v1.6.0 h1:44XHZgF6JIIldRlv+zjg6SygULASmjifnfIQjwCT0e4=
24+
github.com/go-pkgz/routegroup v1.6.0/go.mod h1:Pmu04fhgWhRtBMIJ8HXppnnzOPjnL/IEPBIdO2zmeqg=
2525
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
2626
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
2727
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=

_example/main.go

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"strings"
1313
"time"
1414

15-
"github.com/go-chi/chi/v5"
1615
"github.com/go-oauth2/oauth2/v4/errors"
1716
"github.com/go-oauth2/oauth2/v4/generates"
1817
"github.com/go-oauth2/oauth2/v4/manage"
@@ -22,6 +21,7 @@ import (
2221
log "github.com/go-pkgz/lgr"
2322
"github.com/go-pkgz/rest"
2423
"github.com/go-pkgz/rest/logger"
24+
"github.com/go-pkgz/routegroup"
2525
"github.com/golang-jwt/jwt/v5"
2626
"golang.org/x/oauth2"
2727

@@ -49,7 +49,7 @@ func main() {
4949
AvatarStore: avatar.NewLocalFS("/tmp/demo-auth-service"), // stores avatars locally
5050
AvatarResizeLimit: 200, // resizes avatars to 200x200
5151
ClaimsUpd: token.ClaimsUpdFunc(func(claims token.Claims) token.Claims { // modify issued token
52-
if claims.User != nil && claims.User.Name == "dev_admin" { // set attributes for dev_admin
52+
if claims.User != nil && claims.User.Name == "dev_admin" { // set attributes for dev_admin
5353
claims.User.SetAdmin(true)
5454
claims.User.SetStrAttr("custom-key", "some value")
5555
}
@@ -185,18 +185,18 @@ func main() {
185185
m := service.Middleware()
186186

187187
// setup http server
188-
router := chi.NewRouter()
188+
router := routegroup.New(http.NewServeMux())
189189
// add some external middlewares from go-pkgz/rest
190190
router.Use(rest.AppInfo("auth-example", "umputun", "1.0.0"), rest.Ping)
191191
router.Use(logger.New(logger.Log(log.Default()), logger.WithBody, logger.Prefix("[INFO]")).Handler) // log all http requests
192-
router.Get("/open", openRouteHandler) // open page
193-
router.Group(func(r chi.Router) {
192+
router.HandleFunc("GET /open", openRouteHandler) // open page
193+
router.Group().Route(func(r *routegroup.Bundle) {
194194
r.Use(m.Auth)
195195
r.Use(m.UpdateUser(middleware.UserUpdFunc(func(user token.User) token.User {
196196
user.SetStrAttr("some_attribute", "attribute value")
197197
return user
198198
})))
199-
r.Get("/private_data", protectedDataHandler) // protected api
199+
r.HandleFunc("GET /private_data", protectedDataHandler) // protected api
200200
})
201201

202202
// static files under ~/web
@@ -206,8 +206,8 @@ func main() {
206206

207207
// setup auth routes
208208
authRoutes, avaRoutes := service.Handlers()
209-
router.Mount("/auth", authRoutes) // add auth handlers
210-
router.Mount("/avatar", avaRoutes) // add avatar handler
209+
router.Handle("/auth/", http.StripPrefix("/auth", authRoutes)) // add auth handlers
210+
router.Handle("/avatar/", http.StripPrefix("/avatar", avaRoutes)) // add avatar handler
211211

212212
httpServer := &http.Server{
213213
Addr: ":8080",
@@ -240,9 +240,8 @@ func anonymousAuthProvider() provider.CredCheckerFunc {
240240
}
241241
}
242242

243-
// FileServer conveniently sets up a http.FileServer handler to serve static files from a http.FileSystem.
244-
// Borrowed from https://github.com/go-chi/chi/blob/master/_examples/fileserver/main.go
245-
func fileServer(r chi.Router, path string, root http.FileSystem) {
243+
// fileServer conveniently sets up a http.FileServer handler to serve static files from a http.FileSystem.
244+
func fileServer(r *routegroup.Bundle, path string, root http.FileSystem) {
246245
if strings.ContainsAny(path, "{}*") {
247246
panic("FileServer does not permit URL parameters.")
248247
}
@@ -251,14 +250,11 @@ func fileServer(r chi.Router, path string, root http.FileSystem) {
251250
fs := http.StripPrefix(path, http.FileServer(root))
252251

253252
if path != "/" && path[len(path)-1] != '/' {
254-
r.Get(path, http.RedirectHandler(path+"/", http.StatusMovedPermanently).ServeHTTP)
253+
r.Handle("GET "+path, http.RedirectHandler(path+"/", http.StatusMovedPermanently))
255254
path += "/"
256255
}
257-
path += "*"
258256

259-
r.Get(path, func(w http.ResponseWriter, r *http.Request) {
260-
fs.ServeHTTP(w, r)
261-
})
257+
r.Handle("GET "+path+"{file...}", fs)
262258
}
263259

264260
// GET /open returns a page available without authorization

0 commit comments

Comments
 (0)