Skip to content

Commit f35482b

Browse files
authored
Merge pull request #243 from gofiber/codex/2025-11-30-20-50-34
2 parents cc00b7f + cefa1a6 commit f35482b

File tree

2 files changed

+231
-21
lines changed

2 files changed

+231
-21
lines changed

cmd/internal/migrations/v3/common.go

Lines changed: 108 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -166,48 +166,135 @@ func replaceField(src, field string, fn func(indent, val, comma, comment, newlin
166166
}
167167

168168
func replaceFieldImpl(src, field string, unquote bool, fn func(indent, val, comma, comment, newline string) string) string {
169-
re := regexp.MustCompile(`(?m)^(\s*)` + regexp.QuoteMeta(field) + `:\s*([^\n]+)(\n?)`)
170-
return re.ReplaceAllStringFunc(src, func(s string) string {
171-
sub := re.FindStringSubmatch(s)
172-
indent := sub[1]
173-
val := strings.TrimSpace(sub[2])
174-
newline := sub[3]
175-
176-
comment := ""
177-
if idx := strings.Index(val, "//"); idx >= 0 {
178-
comment = strings.TrimSpace(val[idx:])
179-
val = strings.TrimSpace(val[:idx])
180-
} else if idx := strings.Index(val, "/*"); idx >= 0 {
181-
comment = strings.TrimSpace(val[idx:])
182-
val = strings.TrimSpace(val[:idx])
169+
re := regexp.MustCompile(regexp.QuoteMeta(field) + `:\s*`)
170+
var b strings.Builder
171+
pos := 0
172+
173+
for {
174+
loc := re.FindStringIndex(src[pos:])
175+
if loc == nil {
176+
break
177+
}
178+
loc[0] += pos
179+
loc[1] += pos
180+
181+
start := loc[0]
182+
valStart := loc[1]
183+
184+
prefix := ""
185+
prefixStart := start
186+
if prefixStart > 0 && (src[prefixStart-1] == '{' || src[prefixStart-1] == ',') {
187+
prefix = string(src[prefixStart-1])
188+
prefixStart--
189+
}
190+
191+
indentStart := prefixStart
192+
for indentStart > 0 && (src[indentStart-1] == ' ' || src[indentStart-1] == '\t') {
193+
indentStart--
183194
}
195+
indent := src[indentStart:prefixStart]
184196

197+
b.WriteString(src[pos:indentStart]) //nolint:errcheck // WriteString never returns an error
198+
199+
i := valStart
200+
depth := 0
201+
inString := false
185202
comma := ""
203+
newline := ""
204+
for i < len(src) {
205+
ch := src[i]
206+
if inString {
207+
if ch == '\\' && i+1 < len(src) {
208+
i += 2
209+
continue
210+
}
211+
if ch == '"' {
212+
inString = false
213+
}
214+
i++
215+
continue
216+
}
217+
218+
switch ch {
219+
case '"':
220+
inString = true
221+
case '(', '{', '[':
222+
depth++
223+
case ')', ']':
224+
if depth > 0 {
225+
depth--
226+
}
227+
case '}':
228+
if depth == 0 {
229+
goto endValue
230+
}
231+
depth--
232+
case ',':
233+
if depth == 0 {
234+
comma = ","
235+
suffixStart := i
236+
i = skipCommaSuffix(src, i)
237+
if strings.Contains(src[suffixStart:i], "\n") {
238+
newline = "\n"
239+
}
240+
goto endValue
241+
}
242+
case '\n':
243+
if depth == 0 {
244+
newline = "\n"
245+
i++
246+
goto endValue
247+
}
248+
default:
249+
}
250+
i++
251+
}
252+
253+
endValue:
254+
end := i
255+
val := strings.TrimSpace(src[valStart:end])
256+
val, comment := ExtractCommentAndValue(val)
257+
186258
if strings.HasSuffix(val, ",") {
187-
comma = ","
259+
if comma == "" {
260+
comma = ","
261+
}
188262
val = strings.TrimSpace(strings.TrimSuffix(val, ","))
189263
}
190264

191265
if unquote {
192266
uq, err := strconv.Unquote(val)
193267
if err != nil {
268+
replacement := fmt.Sprintf("%s%s// TODO: migrate %s: %s", prefix, indent, field, val)
194269
if comment != "" {
195-
return fmt.Sprintf("%s// TODO: migrate %s: %s %s%s", indent, field, val, comment, newline)
270+
replacement = fmt.Sprintf("%s %s", replacement, comment)
196271
}
197-
return fmt.Sprintf("%s// TODO: migrate %s: %s%s", indent, field, val, newline)
272+
replacement += newline
273+
b.WriteString(replacement) //nolint:errcheck // WriteString never returns an error
274+
pos = end
275+
continue
198276
}
199277
val = uq
200278
}
201279

202280
repl := fn(indent, val, comma, comment, newline)
281+
var replacement string
203282
if repl == "" {
204283
if comment != "" {
205-
return fmt.Sprintf("%s%s%s", indent, comment, newline)
284+
replacement = fmt.Sprintf("%s%s%s%s", prefix, indent, comment, newline)
285+
} else {
286+
replacement = prefix + newline
206287
}
207-
return newline
288+
} else {
289+
replacement = prefix + repl
208290
}
209-
return repl
210-
})
291+
292+
b.WriteString(replacement) //nolint:errcheck // WriteString never returns an error
293+
pos = end
294+
}
295+
296+
b.WriteString(src[pos:]) //nolint:errcheck // WriteString never returns an error
297+
return b.String()
211298
}
212299

213300
func collectAliases(content string, reImport *regexp.Regexp, defaults []string) []string {

cmd/internal/migrations/v3/jwt_extractor_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,29 @@ var _ = authjwt.New(authjwt.Config{
9494
assert.Contains(t, buf.String(), "Migrating jwt middleware configs")
9595
}
9696

97+
func Test_MigrateJWTExtractor_InlineConfig(t *testing.T) {
98+
t.Parallel()
99+
100+
dir, err := os.MkdirTemp("", "mjwt_inline")
101+
require.NoError(t, err)
102+
defer func() { require.NoError(t, os.RemoveAll(dir)) }()
103+
104+
file := writeTempFile(t, dir, `package main
105+
import jwtware "github.com/gofiber/contrib/jwt"
106+
107+
var _ = jwtware.New(jwtware.Config{TokenLookup: "cookie:jwt"})`)
108+
109+
var buf bytes.Buffer
110+
cmd := newCmd(&buf)
111+
require.NoError(t, v3.MigrateJWTExtractor(cmd, dir, nil, nil))
112+
113+
content := readFile(t, file)
114+
assert.NotContains(t, content, "TokenLookup")
115+
assert.Regexp(t, `Extractor:\s*extractors.FromCookie\("jwt"\)`, content)
116+
assert.Contains(t, content, `"github.com/gofiber/fiber/v3/extractors"`)
117+
assert.Contains(t, buf.String(), "Migrating jwt middleware configs")
118+
}
119+
97120
func Test_MigrateJWTExtractor_ImportWithComment(t *testing.T) {
98121
t.Parallel()
99122

@@ -124,6 +147,76 @@ func JWTMiddleware() fiber.Handler {
124147
assert.Contains(t, buf.String(), "Migrating jwt middleware configs")
125148
}
126149

150+
func Test_MigrateJWTExtractor_FiberV2Middleware(t *testing.T) {
151+
t.Parallel()
152+
153+
dir, err := os.MkdirTemp("", "mjwt_fiber_v2")
154+
require.NoError(t, err)
155+
defer func() { require.NoError(t, os.RemoveAll(dir)) }()
156+
157+
file := writeTempFile(t, dir, `package auth
158+
159+
import (
160+
"os"
161+
"strconv"
162+
163+
jwtware "github.com/gofiber/contrib/jwt"
164+
"github.com/gofiber/fiber/v2"
165+
"github.com/golang-jwt/jwt/v5"
166+
)
167+
168+
// JWT error message.
169+
func jwtError(c *fiber.Ctx, err error) error {
170+
if err.Error() == "Missing or malformed JWT" {
171+
return c.Status(fiber.StatusBadRequest).JSON(&fiber.Map{
172+
"status": "error",
173+
"message": "Missing or malformed JWT!",
174+
})
175+
}
176+
177+
return c.Status(fiber.StatusUnauthorized).JSON(&fiber.Map{
178+
"status": "error",
179+
"message": "Invalid or expired JWT!",
180+
})
181+
}
182+
183+
// Guards a specific endpoint in the API.
184+
func JWTMiddleware() fiber.Handler {
185+
return jwtware.New(jwtware.Config{
186+
ErrorHandler: jwtError,
187+
SigningKey: jwtware.SigningKey{Key: []byte(os.Getenv("JWT_SECRET"))},
188+
TokenLookup: "cookie:jwt",
189+
})
190+
}
191+
192+
// Gets user data (their ID) from the JWT middleware. Should be executed after calling 'JWTMiddleware()'.
193+
func GetDataFromJWT(c *fiber.Ctx) error {
194+
jwtData := c.Locals("user").(*jwt.Token)
195+
claims := jwtData.Claims.(jwt.MapClaims)
196+
parsedUserID := claims["uid"].(string)
197+
userID, err := strconv.Atoi(parsedUserID)
198+
if err != nil {
199+
return c.Status(fiber.StatusInternalServerError).JSON(&fiber.Map{
200+
"status": "fail",
201+
"message": err.Error(),
202+
})
203+
}
204+
205+
c.Locals("currentUser", userID)
206+
return c.Next()
207+
}`)
208+
209+
var buf bytes.Buffer
210+
cmd := newCmd(&buf)
211+
require.NoError(t, v3.MigrateJWTExtractor(cmd, dir, nil, nil))
212+
213+
content := readFile(t, file)
214+
assert.NotContains(t, content, "TokenLookup")
215+
assert.Regexp(t, `Extractor:\s*extractors.FromCookie\("jwt"\)`, content)
216+
assert.Contains(t, content, `"github.com/gofiber/fiber/v3/extractors"`)
217+
assert.Contains(t, buf.String(), "Migrating jwt middleware configs")
218+
}
219+
127220
func Test_MigrateJWTExtractor_LegacyImportPath(t *testing.T) {
128221
t.Parallel()
129222

@@ -157,6 +250,36 @@ func JWTMiddleware() fiber.Handler {
157250
assert.Contains(t, buf.String(), "Migrating jwt middleware configs")
158251
}
159252

253+
func Test_MigrateJWTExtractor_PointerConfig(t *testing.T) {
254+
t.Parallel()
255+
256+
dir, err := os.MkdirTemp("", "mjwt_pointer")
257+
require.NoError(t, err)
258+
defer func() { require.NoError(t, os.RemoveAll(dir)) }()
259+
260+
file := writeTempFile(t, dir, `package main
261+
import (
262+
jwtware "github.com/gofiber/contrib/jwt"
263+
"github.com/gofiber/fiber/v2"
264+
)
265+
266+
func JWTMiddleware() fiber.Handler {
267+
return jwtware.New(&jwtware.Config{
268+
TokenLookup: "cookie:jwt",
269+
})
270+
}`)
271+
272+
var buf bytes.Buffer
273+
cmd := newCmd(&buf)
274+
require.NoError(t, v3.MigrateJWTExtractor(cmd, dir, nil, nil))
275+
276+
content := readFile(t, file)
277+
assert.NotContains(t, content, "TokenLookup")
278+
assert.Regexp(t, `Extractor:\s*extractors.FromCookie\("jwt"\)`, content)
279+
assert.Contains(t, content, `"github.com/gofiber/fiber/v3/extractors"`)
280+
assert.Contains(t, buf.String(), "Migrating jwt middleware configs")
281+
}
282+
160283
func Test_MigrateJWTExtractor_SkipUnrelatedPackage(t *testing.T) {
161284
t.Parallel()
162285

0 commit comments

Comments
 (0)