-
-
Notifications
You must be signed in to change notification settings - Fork 296
refactor: use timestamp+filename for bulk message request ID #903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,19 @@ | ||
| package handlers | ||
|
|
||
| import ( | ||
| "crypto/rand" | ||
| "fmt" | ||
| "path/filepath" | ||
| "regexp" | ||
| "sync" | ||
| "sync/atomic" | ||
| "time" | ||
|
|
||
| "github.com/NdoleStudio/httpsms/pkg/requests" | ||
| "github.com/NdoleStudio/httpsms/pkg/services" | ||
| "github.com/NdoleStudio/httpsms/pkg/telemetry" | ||
| "github.com/NdoleStudio/httpsms/pkg/validators" | ||
| "github.com/davecgh/go-spew/spew" | ||
| "github.com/gofiber/fiber/v2" | ||
| gonanoid "github.com/matoous/go-nanoid/v2" | ||
| "github.com/palantir/stacktrace" | ||
| ) | ||
|
|
||
|
|
@@ -99,7 +100,7 @@ func (h *BulkMessageHandler) Store(c *fiber.Ctx) error { | |
| return h.responseBadRequest(c, err) | ||
| } | ||
|
|
||
| messages, fileType, userLocation, validationErrors := h.validator.ValidateStore(ctx, h.userIDFomContext(c), file) | ||
| messages, userLocation, validationErrors := h.validator.ValidateStore(ctx, h.userIDFomContext(c), file) | ||
| if len(validationErrors) != 0 { | ||
| msg := fmt.Sprintf("validation errors [%s], while sending bulk sms from CSV file [%s] for [%s]", spew.Sdump(validationErrors), file.Filename, h.userIDFomContext(c)) | ||
| ctxLogger.Warn(stacktrace.NewError(msg)) | ||
|
|
@@ -111,7 +112,7 @@ func (h *BulkMessageHandler) Store(c *fiber.Ctx) error { | |
| return h.responsePaymentRequired(c, *msg) | ||
| } | ||
|
|
||
| requestID := h.generateRequestID(fileType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") | ||
| requestID := h.generateRequestID(file.Filename) | ||
| wg := sync.WaitGroup{} | ||
| count := atomic.Int64{} | ||
|
|
||
|
|
@@ -145,21 +146,41 @@ func (h *BulkMessageHandler) Store(c *fiber.Ctx) error { | |
| return h.responseAccepted(c, fmt.Sprintf("Added %d out of %d messages to the queue", count.Load(), len(messages))) | ||
| } | ||
|
|
||
| func (h *BulkMessageHandler) generateRequestID(fileType string, alphabet string) string { | ||
| id, err := gonanoid.Generate(alphabet, 10) | ||
| if err != nil { | ||
| id = h.randomAlphaNum(10, alphabet) | ||
| func (h *BulkMessageHandler) generateRequestID(filename string) string { | ||
| return fmt.Sprintf("bulk-%s-%s", encodeBase62(time.Now().Unix()), truncateFilename(sanitizeFilename(filename), 32)) | ||
| } | ||
|
|
||
| func sanitizeFilename(filename string) string { | ||
| return regexp.MustCompile(`[^a-zA-Z0-9.\-_: ]`).ReplaceAllString(filename, "") | ||
| } | ||
|
|
||
| func encodeBase62(n int64) string { | ||
| const charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | ||
| if n == 0 { | ||
| return "0" | ||
| } | ||
| result := make([]byte, 0, 8) | ||
| for n > 0 { | ||
| result = append(result, charset[n%62]) | ||
| n /= 62 | ||
| } | ||
| // reverse | ||
| for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { | ||
| result[i], result[j] = result[j], result[i] | ||
| } | ||
| return fmt.Sprintf("bulk-%s-%s", fileType, id) | ||
| return string(result) | ||
| } | ||
|
|
||
| func (h *BulkMessageHandler) randomAlphaNum(length int, alphabet string) string { | ||
| b := make([]byte, length) | ||
| if _, err := rand.Read(b); err != nil { | ||
| return alphabet[:length] | ||
| func truncateFilename(filename string, maxLen int) string { | ||
| if len(filename) <= maxLen { | ||
| return filename | ||
| } | ||
| for i := range b { | ||
| b[i] = alphabet[int(b[i])%len(alphabet)] | ||
| ext := filepath.Ext(filename) | ||
| name := filename[:len(filename)-len(ext)] | ||
| available := maxLen - len(ext) | ||
| if available <= 0 { | ||
| return filename[:maxLen] | ||
| } | ||
| return string(b) | ||
| half := available / 2 | ||
| return name[:half] + name[len(name)-(available-half):] + ext | ||
| } | ||
|
Comment on lines
+174
to
186
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. Added a sanitizeFilename function that strips all characters except alphanumeric, dots, and hyphens before embedding the filename in the request ID. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
time.Now().Unix()has one-second granularity, so two uploads of the same filename that arrive within the same second produce an identicalrequestID. All messages from both batches would be tagged with the same ID and become indistinguishable in search and in the history table. The previous nanoid-based scheme avoided this by adding 10 random characters. Adding even 4 random alphanumeric characters after the timestamp (e.g.bulk-{ts}-{file}-{rand4}) would make collisions astronomically unlikely while keeping the human-readable filename benefit.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Added a 4-character random base62 suffix after the timestamp, making the format \�ulk-{base62_ts}{rand4}-{filename}. This makes same-second collisions astronomically unlikely while keeping the human-readable filename.