| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Not up to standards ⛔🔴 Issues 50 minor🟢 Metrics 584 complexity · 215 duplication
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer |
Sorry, something went wrong.
Greptile SummaryThis PR introduces a full contacts management feature: a new Contact entity with CRUD endpoints, CSV import, cached phone-to-contact name resolution in message threads, and a server-side-paginated contacts page in the web UI.
Confidence Score: 3/5Safe to merge with caution — one data-retention gap needs to be addressed before this goes to production for any deployment that supports account deletion. The contacts feature is well-structured and thoroughly tested, but DeleteAllForUser is wired to the repository interface and never called on account deletion, unlike every other entity in the codebase. Users who delete their accounts will have their contacts silently retained in the database. Additionally, the ILIKE search passes raw user input as a LIKE pattern without escaping % and _, so a search for _ returns all contacts. api/pkg/repositories/contact_repository.go and the missing contacts listener file (no listener for UserAccountDeleted was added); api/pkg/repositories/gorm_contact_repository.go for the unescaped LIKE wildcards in the search query. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant ContactHandler
participant ContactHandlerValidator
participant ContactService
participant ContactRepository
participant Cache
Client->>ContactHandler: POST /v1/contacts (JSON array or object)
ContactHandler->>ContactHandlerValidator: ValidateStore(request)
ContactHandlerValidator-->>ContactHandler: url.Values (errors)
ContactHandler->>ContactService: CreateMany(userID, contacts)
ContactService->>ContactRepository: Store(contacts)
ContactRepository-->>ContactService: nil
ContactService->>Cache: Set(key, empty, 24h) [invalidate]
ContactService-->>ContactHandler: nil
ContactHandler-->>Client: 201 Created
Client->>ContactHandler: "GET /v1/threads?contacts=true"
ContactHandler->>MessageThreadService: "GetThreads(WithContacts=true)"
MessageThreadService->>ContactService: GetContactMap(userID)
ContactService->>Cache: Get(key)
alt cache hit
Cache-->>ContactService: JSON map
ContactService-->>MessageThreadService: "map[phone]*Contact"
else cache miss or invalidated
Cache-->>ContactService: empty or error
ContactService->>ContactRepository: FetchAll(userID)
ContactRepository-->>ContactService: []Contact
ContactService->>Cache: Set(key, encodedMap, 24h)
ContactService-->>MessageThreadService: "map[phone]*Contact"
end
MessageThreadService-->>ContactHandler: []MessageThread with ContactDetails
ContactHandler-->>Client: 200 OK
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant ContactHandler
participant ContactHandlerValidator
participant ContactService
participant ContactRepository
participant Cache
Client->>ContactHandler: POST /v1/contacts (JSON array or object)
ContactHandler->>ContactHandlerValidator: ValidateStore(request)
ContactHandlerValidator-->>ContactHandler: url.Values (errors)
ContactHandler->>ContactService: CreateMany(userID, contacts)
ContactService->>ContactRepository: Store(contacts)
ContactRepository-->>ContactService: nil
ContactService->>Cache: Set(key, empty, 24h) [invalidate]
ContactService-->>ContactHandler: nil
ContactHandler-->>Client: 201 Created
Client->>ContactHandler: "GET /v1/threads?contacts=true"
ContactHandler->>MessageThreadService: "GetThreads(WithContacts=true)"
MessageThreadService->>ContactService: GetContactMap(userID)
ContactService->>Cache: Get(key)
alt cache hit
Cache-->>ContactService: JSON map
ContactService-->>MessageThreadService: "map[phone]*Contact"
else cache miss or invalidated
Cache-->>ContactService: empty or error
ContactService->>ContactRepository: FetchAll(userID)
ContactRepository-->>ContactService: []Contact
ContactService->>Cache: Set(key, encodedMap, 24h)
ContactService-->>MessageThreadService: "map[phone]*Contact"
end
MessageThreadService-->>ContactHandler: []MessageThread with ContactDetails
ContactHandler-->>Client: 200 OK
Reviews (1): Last reviewed commit: "fix(web): mark contacts headers unsortab..." | Re-trigger Greptile |
Sorry, something went wrong.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8dd82cae-8bc6-4eaa-9b90-95073720c577
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move sanitizeUniqueStrings into the shared requests helper. Preserve first-seen ordering after normalization. Add a direct ToContacts nil-properties regression test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allow .csv extension only; accept common browser MIME fallbacks for uploads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add contacts query parsing and optional ContactDetails enrichment through a contact map provider. DI passes nil until Task 8 wires ContactService. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ContactHandler with authenticated /v1/contacts routes: GET/POST /v1/contacts, POST /v1/contacts/upload, PUT/DELETE /v1/contacts/:contactID. - Load user-scoped contact before Update/Delete so a missing or cross-user contact returns 404 (avoids the repo's zero-row Delete returning 204 while advertising 404). - Support one-and-many JSON create via ContactStoreRequest and reuse ContactService.CreateMany so cache invalidation stays single-shot for Store and CSV upload alike. - Add DI getters ContactRepository, ContactService, ContactHandlerValidator, ContactHandler and RegisterContactRoutes; register the routes alongside RegisterMessageThreadRoutes. - Finalise Task 7 by injecting container.ContactService() into MessageThreadService, replacing the temporary nil dependency. - Cover all five routes with real Fiber requests plus a shared ContactRepository fake that verifies service/repository effects, and pin the DI wiring with a compile-time test that constructs MessageThreadService with the ContactService. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make emails/properties optional in contact request DTOs. Regenerate Swagger docs and add request JSON regression tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ialogs Add the /contacts page grounded in the existing Vuetify theme. Header uses text-display-large; debounced search drives the API ?query= param. VDataTable columns: Name, Phone Numbers, Emails, Created, Updated, Actions. Rows show avatar initials, phone chips and relative timestamps via humanizeTime. Add/edit/delete/import-CSV dialogs use opacity 0.9 and warning-colored Close controls. Add/Edit has repeatable phone numbers, emails and free-form properties, preserved on edit. CSV import surfaces row-indexed API validation errors inline, not only via a toast. loadContacts forwards the trimmed search value as the query param, keeping Task 11's contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Preserve contact details across mark-read updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extend the contact repository/service/handler so GET /v1/contacts returns a top-level total count for the same user/query filter, independent of skip/limit, enabling true server-side pagination. - add ContactRepository.Count reusing a shared scopedContactQuery helper so Index and Count filters can never drift; Count ignores limit/offset - add ContactService.Count and handler responseOKWithTotal; Index returns total - add Total to responses.ContactsResponse and regenerate Swagger - sanitize parsed CSV rows before validation so CSV and JSON accept the same phone/email formats; drop the now-redundant re-sanitize on upload - assert Scan error behaviour instead of the unexported stacktrace type - tests for count filter parity, total propagation, and CSV sanitization Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- store total now reflects the server count; loadContacts accepts skip and limit and remembers the current window so mutation refreshes stay in place - contacts page uses VDataTableServer with items-length bound to the server total; page/size changes fetch the correct skip/limit and a debounced query resets to page 1 without duplicate requests - delete bumps the load generation so an in-flight load cannot resurrect a deleted contact, without leaving loading stuck or hiding delete errors - remove the dead filteredContacts computed and add defensive null coalescing for emails/phone_numbers in the table display - regenerate API models with the new contacts total field Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove misleading sort affordances from the contacts table. Keep the subtitle aligned with the active search state. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1f3addc9-71dd-4cb7-bfae-e6fdcc8511af
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Improve the contacts table layout, actions, relative timestamps, and header emphasis. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4a93bfbb-739a-414c-8eb4-48b15a69fb9d
Propagate contact table sorting through the API and repository while keeping contact lookups on the process-local cache. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dec094f9-5386-4607-998a-1701ac10b7cf
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Subscribe contact cleanup to account deletion events so user contact data is removed from storage and invalidated from the contact-map cache. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dec094f9-5386-4607-998a-1701ac10b7cf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dec094f9-5386-4607-998a-1701ac10b7cf
There was a problem hiding this comment.
Adds full-stack contact management and resolves saved contact names in message threads.
Changes:
Copilot reviewed 71 out of 73 changed files in this pull request and generated 9 comments.
Show a summary per file| File | Description |
|---|---|
| .mcp.json | Simplifies MCP configuration. |
| api/docs/docs.go | Regenerates embedded Swagger documentation. |
| api/docs/swagger.json | Documents contact APIs and thread enrichment. |
| api/docs/swagger.yaml | Documents contact APIs and thread enrichment. |
| api/pkg/di/container.go | Wires contact layers, routes, listeners, and migration. |
| api/pkg/entities/contact.go | Defines the contact entity and properties. |
| api/pkg/entities/contact_test.go | Tests contact property serialization. |
| api/pkg/entities/message_thread.go | Adds transient resolved contact details. |
| api/pkg/entities/message_thread_test.go | Tests transient contact serialization. |
| api/pkg/handlers/contact_handler.go | Implements contact HTTP endpoints. |
| api/pkg/handlers/contact_handler_test.go | Tests contact endpoint behavior. |
| api/pkg/handlers/handler.go | Adds responses containing pagination totals. |
| api/pkg/handlers/message_thread_handler.go | Documents contact enrichment query support. |
| api/pkg/handlers/message_thread_handler_contacts_test.go | Tests enriched thread responses. |
| api/pkg/handlers/message_thread_handler_test.go | Updates service construction. |
| api/pkg/listeners/contact_listener.go | Deletes contacts with user accounts. |
| api/pkg/listeners/contact_listener_test.go | Tests contact cleanup events. |
| api/pkg/listeners/message_thread_listener_test.go | Updates service construction. |
| api/pkg/repositories/contact_repository.go | Defines contact persistence operations. |
| api/pkg/repositories/gorm_contact_repository.go | Implements contact queries and persistence. |
| api/pkg/repositories/gorm_contact_repository_test.go | Tests generated contact queries. |
| api/pkg/requests/contact_index.go | Defines contact list parameters. |
| api/pkg/requests/contact_store.go | Defines and normalizes contact creation. |
| api/pkg/requests/contact_store_test.go | Tests contact request transformations. |
| api/pkg/requests/contact_update.go | Defines contact updates. |
| api/pkg/requests/message_thread_index_request.go | Adds contact enrichment parameters. |
| api/pkg/requests/message_thread_index_request_test.go | Tests enrichment parameter handling. |
| api/pkg/requests/request.go | Adds normalized string deduplication. |
| api/pkg/responses/contact_responses.go | Defines contact response schemas. |
| api/pkg/services/contact_service.go | Implements contact operations and resolution cache. |
| api/pkg/services/contact_service_test.go | Tests contact service and caching behavior. |
| api/pkg/services/entitlement_service.go | Supports batch contact limits. |
| api/pkg/services/entitlement_service_test.go | Tests contact entitlement limits. |
| api/pkg/services/message_thread_service.go | Resolves contacts for threads. |
| api/pkg/services/message_thread_service_contacts_test.go | Tests contact resolution. |
| api/pkg/services/message_thread_service_test.go | Updates service construction. |
| api/pkg/validators/bulk_message_handler_validator.go | Removes unused cache dependency. |
| api/pkg/validators/contact_handler_validator.go | Validates contact JSON and CSV input. |
| api/pkg/validators/contact_handler_validator_test.go | Tests contact validation and CSV parsing. |
| api/pkg/validators/message_handler_validator.go | Removes constructor cache dependency. |
| docs/superpowers/specs/2026-08-21-contact-phone-input-design.md | Documents phone input design. |
| docs/superpowers/specs/2026-08-21-contacts-table-header-design.md | Documents table header styling. |
| tests/README.md | Records Contacts E2E coverage. |
| tests/contacts_integration_test.go | Tests CRUD, import, search, and resolution. |
| web/app/components/MessageThread.vue | Displays resolved contact names and initials. |
| web/app/components/MessageThreadHeader.vue | Adds Contacts navigation. |
| web/app/composables/useFilters.ts | Exposes additional filter helpers. |
| web/app/pages/billing/index.vue | Uses prefixed Vuetify display composable. |
| web/app/pages/blog/end-to-end-encryption-to-sms-messages.vue | Updates Vuetify composable usage. |
| web/app/pages/blog/forward-incoming-sms-from-phone-to-webhook.vue | Updates Vuetify composable usage. |
| web/app/pages/blog/grant-send-and-read-sms-permissions-on-android.vue | Updates Vuetify composable usage. |
| web/app/pages/blog/how-to-send-sms-messages-from-excel.vue | Updates Vuetify composable usage. |
| web/app/pages/blog/index.vue | Updates Vuetify composable usage. |
| web/app/pages/blog/send-bulk-sms-from-csv-file-with-no-code.vue | Updates Vuetify composable usage. |
| web/app/pages/blog/send-sms-from-android-phone-with-python.vue | Updates Vuetify composable usage. |
| web/app/pages/blog/send-sms-when-new-row-is-added-to-google-sheets-using-zapier.vue | Updates Vuetify composable usage. |
| web/app/pages/bulk-messages/index.vue | Uses prefixed display composable. |
| web/app/pages/contacts/index.vue | Adds the Contacts management page. |
| web/app/pages/heartbeats/[id].vue | Uses prefixed display composable. |
| web/app/pages/index.vue | Uses prefixed display composable. |
| web/app/pages/messages/index.vue | Uses prefixed display composable. |
| web/app/pages/phone-api-keys/index.vue | Uses prefixed display composable. |
| web/app/pages/search-messages/index.vue | Uses prefixed display composable. |
| web/app/pages/settings/index.vue | Uses prefixed display composable. |
| web/app/pages/threads/[id]/index.vue | Displays resolved contact names. |
| web/app/pages/threads/index.vue | Uses prefixed display composable. |
| web/app/stores/contacts.ts | Manages contact API state and mutations. |
| web/app/stores/threads.ts | Requests and preserves contact enrichment. |
| web/app/utils/filters.ts | Adds compact relative-time formatting. |
| web/nuxt.config.ts | Enables prefixed Vuetify composables. |
| web/public/templates/httpsms-contacts.csv | Adds a contact import template. |
| web/shared/types/api.ts | Adds generated contact API types. |
api/pkg/handlers/contact_handler.go:164
// @Param document formData file true "CSV file of contacts"
// @Success 201 {object} responses.ContactsResponse
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Sorry, something went wrong.
Fetch and cache only phone numbers present in the current thread page. Keep cache entries consistent across mutations and include the staged contact form UX updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ffe6b904-8f76-4e89-a786-d07a0cc9096d
Stabilize asynchronous integration flows. Keep contact pagination, resolution, and generated API contracts deterministic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 58beb14a-b8b6-4d9c-a27d-5e70118cef95
| Back | FazBrowse Home | New Git URL |
Summary
Testing