v0.4.5 - Calendar View, iCal Export & More #54
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "v0.4.5"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
🎉 Release v0.4.5
This PR includes all the features and improvements for v0.4.5:
✨ New Features
🐛 Bug Fixes
📝 Resolved Issues
See the full release notes in
plans/v0.4.5_RELEASE_NOTES.mdfor complete details.Ready for review and merge!
Pull Request Overview
This release adds several major features to SubTrackr focused on calendar visualization, icon display, email notifications, and table sorting capabilities.
Key Changes:
Reviewed Changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 13 comments.
Show a summary per file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The template function
intis defined twice - once in the main function and again in theloadTemplatesfunction. This creates code duplication. Consider extracting the template functions map into a shared variable or function to avoid duplication.@ -154,0 +166,4 @@}log.Println("Running migration: Adding subscription icon URLs...")The migration uses SQLite-specific syntax
pragma_table_infowhich will fail on other database engines. While the project currently uses SQLite, this creates a hard dependency. Consider using GORM's cross-databaseMigrator().HasColumn()method instead for better portability.@ -24,107 +25,156 @@ func NewSettingsHandler(service *service.SettingsService) *SettingsHandler {// SaveSMTPSettings saves SMTP configurationThe test SMTP connection function doesn't validate the
Toemail field before testing, but this field is used in production email sending. While it checks Host, Port, Username, and Password, the missing validation forTocould lead to confusion when the test passes but actual emails fail due to missing recipient. Consider addingconfig.To == ""to the validation check.The error message states "All SMTP fields are required (including To email)" but this doesn't mention FromName which is also being parsed and could be required in some contexts. Consider clarifying which fields are truly required vs optional in the error message.
@ -104,2 +99,2 @@})returnauth := smtp.PlainAuth("", config.Username, config.Password, config.Host)This assignment to To is useless since its value is never read.
@ -176,3 +359,4 @@"DarkMode": h.settingsService.IsDarkModeEnabled(),"Version": version.GetVersion(),"SMTPConfig": smtpConfig,})Potential XSS vulnerability: The
EventsByDateis serialized to JSON and then cast totemplate.JSwithout proper sanitization. While JSON encoding provides some protection, event names containing</script>or other HTML/JS sequences could potentially break out of the JavaScript context. Consider using proper HTML escaping or validating the JSON output before injecting into the template.Potential XSS vulnerability: User-supplied data from
sub.Nameandsub.URLare being interpolated directly into the iCal description without proper escaping. The description field construction usesfmt.Sprintfwith unescaped strings. While the comment mentions "should escape commas, semicolons, etc.", this hasn't been implemented. iCal format requires escaping special characters like commas, semicolons, backslashes, and newlines to prevent format corruption or potential security issues.@ -93,3 +142,3 @@}// Also calculate if renewal date is nil and status is Active// Calculate if renewal date is nil and status is ActiveThe AfterFind hook modifies the database using
tx.Model(s).UpdateColumnwhich could cause performance issues when loading many subscriptions at once (N+1 updates). This also means every time subscriptions are fetched, there could be database writes. Consider using a scheduled background job or manual update trigger instead of automatic updates on every database read.@ -90,3 +132,4 @@func (r *SubscriptionRepository) GetByID(id uint) (*models.Subscription, error) {var subscription models.Subscriptionif err := r.db.Preload("Category").First(&subscription, id).Error; err != nil {[nitpick] The
GetAllSortedfunction is vulnerable to SQL injection through thesortColumnvariable. While there is validation against a whitelist (validSortColumns), thesortColumnvalue is then directly concatenated into the SQL ORDER BY clause on line 120. For the "category" sort, this could be exploited if the validation logic has any flaws. Consider using GORM's safer ordering methods or ensure the validation is airtight. The current implementation appears safe due to the whitelist, but it's a pattern that could be fragile if modified.[nitpick] The SQL query uses a long CASE statement with hardcoded schedule types. After adding "Quarterly", this same pattern needs updating. The query has been updated correctly, but consider extracting schedule-to-multiplier logic into a database function or Go function to avoid maintaining this logic in multiple places (this appears in at least 3 places: model methods, handler, and this query).
@ -0,0 +156,4 @@if err != nil {return fmt.Errorf("failed to close writer: %w", err)}}The SSL/TLS connection handling has code duplication between lines 44-101 and lines 107-158. The message building logic (lines 81-92 and 138-149) is duplicated. Consider extracting the common message building and sending logic into a helper function to improve maintainability.
@ -0,0 +84,4 @@}return fetchedURL}The LogoService makes external HTTP requests to fetch favicons without any rate limiting or caching mechanism. This could lead to performance issues and potential abuse if many subscriptions are created/updated rapidly. Consider implementing caching (e.g., in-memory cache with TTL) or rate limiting for external requests.
@ -0,0 +226,4 @@${iconHtml}<span class="truncate">${eventName}</span></span><span class="ml-2 flex-shrink-0 font-medium">${currencySymbol}${cost}</span></button>`;The calendar JavaScript performs client-side HTML construction with user data that uses basic escaping (
.replace(/"/g, '"').replace(/'/g, ''')). However, this doesn't escape other potentially dangerous characters like<,>, or&itself. While theiconURLescaping is done, the event name could still contain HTML that breaks the layout or causes issues. Consider using proper HTML escaping or the browser'stextContentproperty when creating elements.@ -104,2 +99,2 @@})returnauth := smtp.PlainAuth("", config.Username, config.Password, config.Host)invalid, ignoring