Skip to content

Improve account update and chat cleanup#676

Merged
feruzm merged 7 commits intodevelopfrom
remeda
Feb 25, 2026
Merged

Improve account update and chat cleanup#676
feruzm merged 7 commits intodevelopfrom
remeda

Conversation

@feruzm
Copy link
Copy Markdown
Member

@feruzm feruzm commented Feb 25, 2026

Summary by CodeRabbit

  • New Features

    • Admin POST endpoint to run cleanup of inactive Mattermost users with configurable threshold.
    • Ability to reactivate previously deactivated Mattermost users.
    • Automated inactive-user detection and deactivation based on activity lookups.
  • Bug Fixes

    • Bootstrap process now tolerates per-channel failures and continues partial progress.
    • Wallet asset lookup uses smarter fallback and price handling to avoid incorrect errors.
  • Refactor

    • Removed remeda dependency and replaced library cloning with JSON-based deep clones across SDK modules.
  • Documentation

    • Profile metadata: explicit token handling, safer merges, stricter parsing/versioning.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an admin POST endpoint to purge inactive Mattermost users, server-side reactivate/cleanup logic with Hive activity lookups and batch deactivation, makes Mattermost bootstrap resilient to per-channel failures, removes Remeda usages replacing deep clones with JSON cloning, enhances profile-metadata merging/tokens handling, and adjusts wallet asset fallback logic.

Changes

Cohort / File(s) Summary
Mattermost Cleanup API & Server
apps/web/src/app/api/mattermost/admin/cleanup-inactive/route.ts, apps/web/src/server/mattermost.ts
Adds admin-only POST endpoint validating super-admin and calling cleanupInactiveMattermostUsers(inactiveDays); server adds delete_at to MattermostUser, reactivateMattermostUser(), cleanupInactiveMattermostUsers() with paged team membership, Hive profile lookups, batching, and random password generation for new users.
Mattermost Bootstrap Resilience
apps/web/src/app/api/mattermost/bootstrap/route.ts
Replaces per-channel Promise.all with batched Promise.allSettled; aggregates per-batch failures, logs warnings instead of aborting, and makes explicit-community join non-blocking.
Remeda Removal & Account Mutations
packages/sdk/package.json, packages/sdk/tsup.config.ts, packages/sdk/src/modules/accounts/mutations/*.ts
Removes remeda from devDependencies and tsup externals; replaces Remeda-based deep clones with JSON.parse(JSON.stringify(...)) across multiple account mutation utilities.
Profile Metadata Utilities
packages/sdk/src/modules/accounts/utils/profile-metadata.ts
Adds isPlainObject, deepMerge, DENIED_KEYS, and sanitizeTokens; buildProfileMetadata now enforces version = 2, accepts `tokens?: ProfileTokens
Wallet Asset Fallback Logic
packages/sdk/src/modules/wallet/queries/get-account-wallet-asset-info-query-options.ts
Adds early-return when portfolioAssetInfo.price > 0; falls back to portfolio data if Hive Engine asset missing; merges fetched price into portfolio info when appropriate; handles non-standard assets with portfolio fallback.
Misc SDK changes
packages/sdk/src/...
Minor updates removing Remeda imports, swapping cloning approach, and small parse/logging behavior changes across SDK modules.

Sequence Diagram

sequenceDiagram
    participant Client as API Client
    participant Endpoint as cleanup-inactive Endpoint
    participant Server as App Server
    participant MmAPI as Mattermost Admin API
    participant HiveAPI as Hive Bridge API

    Client->>Endpoint: POST /api/mattermost/admin/cleanup-inactive (inactiveDays?)
    Endpoint->>Endpoint: read auth token from cookies
    Endpoint->>MmAPI: GET /users/me (validate super-admin)
    MmAPI-->>Endpoint: current user
    Endpoint->>Server: call cleanupInactiveMattermostUsers(inactiveDays)

    Server->>MmAPI: list team members / users (paged)
    MmAPI-->>Server: team members (with delete_at)
    Server->>HiveAPI: hiveGetProfiles(usernames batch)
    HiveAPI-->>Server: profiles (last activity timestamps)

    Server->>Server: compute inactive users (cutoff)
    Server->>MmAPI: deactivate users (admin API) / reactivate if needed
    MmAPI-->>Server: deactivation/reactivation results

    Server-->>Endpoint: {deactivated, checked, skipped, errors}
    Endpoint-->>Client: JSON response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 I hopped through branches, nudged the code hay,
JSON-clones hum where Remeda hopped away,
Sleeping users probed by Hive's soft light,
Bootstrap keeps hopping when channels pluck and flight,
Tokens tidy, meadow neat — ready for new day. 🌼

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is partially related to the changeset. It mentions 'account update' and 'chat cleanup' which are present in the changes, but the primary focus of the PR is removing the remeda dependency across multiple files and refactoring related cloning logic, which the title does not capture. Consider a more descriptive title that reflects the main change, such as 'Remove remeda dependency and refactor deep cloning' to better represent the primary objective of the changeset.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch remeda

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@feruzm feruzm added the patch Bug fixes and patches (1.0.0 → 1.0.1), add this only if any packages/ have patch changes in PR label Feb 25, 2026
@feruzm feruzm added patch Bug fixes and patches (1.0.0 → 1.0.1), add this only if any packages/ have patch changes in PR and removed patch Bug fixes and patches (1.0.0 → 1.0.1), add this only if any packages/ have patch changes in PR labels Feb 25, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
apps/web/src/server/mattermost.ts (3)

807-813: Redundant user lookup on each deactivation — O(2N) API calls where O(N) suffices.

deactivateMattermostUserAsAdmin(account.name) internally calls findMattermostUser to resolve the username → user ID, but you already resolved these IDs in the batch at Line 778. Consider extracting a lower-level helper that deactivates by user ID directly, avoiding one extra Mattermost API round-trip per deactivation.

♻️ Proposed approach

Add a by-ID deactivation helper and use it in the cleanup loop:

+async function deactivateMattermostUserById(userId: string): Promise<void> {
+  await mmFetch(`/users/${userId}`, {
+    method: "DELETE",
+    headers: getAdminHeaders()
+  });
+}

Then in the cleanup loop, build a name→id map from the already-fetched users array and use it:

+    const userIdByName = new Map(users.map((u) => [u.username, u.id]));
     // ...
-              await deactivateMattermostUserAsAdmin(account.name);
+              const uid = userIdByName.get(account.name);
+              if (uid) {
+                await deactivateMattermostUserById(uid);
+              }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/server/mattermost.ts` around lines 807 - 813, The loop calls
deactivateMattermostUserAsAdmin(account.name) which re-resolves username→ID via
findMattermostUser causing redundant API calls; add a new lower-level helper
(e.g., deactivateMattermostUserById or deactivateUserById) that accepts a
Mattermost user ID and performs the deactivation, build a name→id map from the
already-fetched users array (the batch created around Line 778), then in the
cleanup loop use map[account.name] to get the ID and call the new by-ID helper
(fall back to existing deactivateMattermostUserAsAdmin only if the ID is
missing) so each user is deactivated with a single API request.

16-21: Server and client MattermostUser interfaces are diverging.

The server-side MattermostUser now includes email and delete_at, while the client-side definition in apps/web/src/features/chat/mattermost-api.ts has neither. This is likely intentional (admin vs. user context), but worth noting to avoid accidental cross-imports. Consider adding a brief doc comment to clarify the server-only scope.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/server/mattermost.ts` around lines 16 - 21, The server-side
MattermostUser interface now includes email and delete_at, which diverges from
the client-side definition; add a short doc comment above the MattermostUser
interface declaration that states this is a server-only/admin context type (not
to be imported by client code) and briefly list the extra fields (email,
delete_at) to prevent accidental cross-imports; update the comment near the
MattermostUser symbol to make the scope explicit and mention where the
client-side type lives so reviewers/maintainers can quickly see the intended
separation.

768-823: Sequential deactivation could be slow for large cleanup runs.

Each deactivation is awaited individually inside a for…of loop. For a team with many inactive users, this could take a long time. Consider parallelizing with bounded concurrency (e.g., batches of 5–10 Promise.allSettled calls), similar to the pattern already used in deleteMattermostDmPostsByUserAsAdmin (Line 504–519).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/server/mattermost.ts` around lines 768 - 823, The sequential
deactivation loop is slow; replace the inner for...of that calls
deactivateMattermostUserAsAdmin(account.name) with a bounded-concurrency
batching approach (e.g., split accounts toDeactivate into chunks of 5–10 and use
Promise.allSettled per chunk) so multiple deactivations run in parallel; after
each batch, inspect results to increment deactivated and errors counters
accordingly (increment deactivated for fulfilled promises, errors for rejected)
while preserving skipped/checked logic and keeping hiveGetProfiles,
deactivateMattermostUserAsAdmin, checked, deactivated, and errors as the
referenced symbols to update.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/src/server/mattermost.ts`:
- Around line 807-813: The loop calls
deactivateMattermostUserAsAdmin(account.name) which re-resolves username→ID via
findMattermostUser causing redundant API calls; add a new lower-level helper
(e.g., deactivateMattermostUserById or deactivateUserById) that accepts a
Mattermost user ID and performs the deactivation, build a name→id map from the
already-fetched users array (the batch created around Line 778), then in the
cleanup loop use map[account.name] to get the ID and call the new by-ID helper
(fall back to existing deactivateMattermostUserAsAdmin only if the ID is
missing) so each user is deactivated with a single API request.
- Around line 16-21: The server-side MattermostUser interface now includes email
and delete_at, which diverges from the client-side definition; add a short doc
comment above the MattermostUser interface declaration that states this is a
server-only/admin context type (not to be imported by client code) and briefly
list the extra fields (email, delete_at) to prevent accidental cross-imports;
update the comment near the MattermostUser symbol to make the scope explicit and
mention where the client-side type lives so reviewers/maintainers can quickly
see the intended separation.
- Around line 768-823: The sequential deactivation loop is slow; replace the
inner for...of that calls deactivateMattermostUserAsAdmin(account.name) with a
bounded-concurrency batching approach (e.g., split accounts toDeactivate into
chunks of 5–10 and use Promise.allSettled per chunk) so multiple deactivations
run in parallel; after each batch, inspect results to increment deactivated and
errors counters accordingly (increment deactivated for fulfilled promises,
errors for rejected) while preserving skipped/checked logic and keeping
hiveGetProfiles, deactivateMattermostUserAsAdmin, checked, deactivated, and
errors as the referenced symbols to update.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f87fd4 and 7c0ef93.

📒 Files selected for processing (1)
  • apps/web/src/server/mattermost.ts

@feruzm feruzm merged commit f6b390d into develop Feb 25, 2026
1 check passed
@feruzm feruzm deleted the remeda branch February 25, 2026 19:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch Bug fixes and patches (1.0.0 → 1.0.1), add this only if any packages/ have patch changes in PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant